;
; PROGRAM:  DU3
; AUTHOR:  RICHARD CONN
; DERIVATION:  DUTIL is derived from DU Version 7.5
;		DU2 is derived from DUTIL Version 1.1
;		DU3 is derived from DU2 Version 1.1
; VERSION:  1.0
; DATE:  20 June 84
; PREVIOUS VERSIONS:  None
; NOTE:  DU3 must be assembled using M80 (or equiv)
;
VERS	EQU	10
Z3ENV	EQU	0F400H

;
;  DU3 is derived from --
;	  DU.ASM  V7.5	Revised 1/23/81
;	DISK UTILITY - By Ward Christensen
;
;  Principal Authors of DU V7.5 are --
;	WLC   KBP   RGF   BRR
;
;  Key comments from DU V7.5 and DU3 follow --
;
;This version of DU is compatible with CP/M 2.x
;and does not require alteration for various hardware
;configurations.  It adjusts itself automatically to
;the correct number of sectors, tracks, directory size,
;etc.  It has been tested on 5-1/4" and 8" floppy, and
;10 megabyte hard disk systems.
;
;Because of the automatic adaption feature, no conditional
;assembly options are included.
;
;*************************************************
;* 						 *
;*   This program has been heavily modified	 *
;* to allow it to work without modification	 *
;* on all versions of CP/M 2.x.			 *
;*   One known possible problem involves the	 *
;* system tracks on some systems, and results	 *
;* from the system sectors being skewed. There	 *
;* is NO way for a program executing under CP/M	 *
;* to know about this.  This program assumes the *
;* standard convention of no skew being used on	 *
;: the system tracks. This usually isn't a prob- *
;* lem because the SYSGEN program can be used to *
;* get the system from the disk so that	it can	 *
;* be modified.					 *
;* 						 *
;* 		Ron Fowler			 *
;* 						 *
;*************************************************
;

;
;  SYSLIB and Z3LIB References
;
	ext	z3vinit,envptr,cls,at,tinit,dinit,stndout,stndend,gotoxy
	ext	getspeed,getcrt,getmdisk,getmuser
	ext	codend

;
;System equates
;
BASE	EQU	0	;SET TO 4200H FOR HEATH OR TRS-80 ALTCPM
;
;CP/M Key Areas
;
FCB	EQU	BASE+5CH	;CP/M FCB
BDOS	EQU	BASE+5		;CP/M BDOS ENTRY POINT
TBUFF	EQU	BASE+80H	;CP/M TEMPORARY DISK I/O BUFFER
TPA	EQU	BASE+100H	;CP/M TRANSCIENT PROGRAM AREA
;
;  Some Key Variables in DU3
;
EOLCH	equ	','		;Marks logical end of line
SEPCH	equ	' '		;Argument Separator
MULCH	equ	'*'		;Multiplication Command
DIM	equ	1		;Enter DIM Mode for ILPRT
BRIGHT	equ	2		;Enter BRIGHT Mode for ILPRT

;
;CP/M BDOS Function Codes
;
PRINT	EQU	9
GVERS	EQU	12
RESETDK EQU	13	;RESET SYSTEM
SELDK	EQU	14	;SELECT DISK
CLOSEF	EQU	16	;CLOSE FILE
SRCHF	EQU	17	;SEARCH FIRST
SRCHN	EQU	18	;SEARCH NEXT
DELF	EQU	19	;DELETE FILE
WRITEF	EQU	21	;WRITE BLOCK TO FILE
MAKEF	EQU	22	;CREATE FILE
SUSER	EQU	32	;SELECT USER
GETDSK	EQU	25
GETDPB	EQU	31
;
;CP/M 1.4 Offsets and Some Key Values
;
TRNOFF	EQU	15	;CP/M 1.4 OFFSET FROM BASE
			;OF BDOS TO SECTRAN ROUTINE
SKWOFF	EQU	1AH	;CP/M 1.4 OFFSET TO SKEW TABLE
S2OFF	EQU	14	;OFFSET INTO FCB FOR S2 BYTE
DPBOFF	EQU	3AH	;CP/M 1.4 OFFSET TO DPB WITHIN BDOS
S2MASK	EQU	0FH	;MASK FOR EXTENDED RC BITS OF S2
DPBLEN	EQU	15	;SIZE OF CP/M 2.x DISK PARM BLOCK
;
;Define ASCII characters
;
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
TAB	EQU	09H	;TAB
BS	EQU	08H	;BACKSPACE

;
;   MACROS INCLUDE:
;
;	DJNZ	- DECREMENT B AND JUMP RELATIVE IF NO ZERO
;
DJNZ	MACRO	?N	;;DECREMENT B AND JUMP ON NO ZERO
	 DCR	B
	 JNZ	?N
	ENDM
;
; END OF MACROS
;


;
;Beginning of Program
;
;
; Environment Definition
;
	if	z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
	jmp	start
	db	'Z3ENV'	;This is a ZCPR3 Utility
	db	1	;External Environment Descriptor
z3eadr:
	dw	z3env
start:
	lhld	z3eadr	;pt to ZCPR3 environment
;
	else
;
; Internal ZCPR3 Environment Descriptor
;
	MACLIB	Z3BASE.LIB
	MACLIB	SYSENV.LIB
z3eadr:
	jmp	start
	SYSENV
start:
	lxi	h,z3eadr	;pt to ZCPR3 environment
	endif

;
; Start of Program -- Initialize ZCPR3 Environment
;
	call	z3vinit	;initialize the ZCPR3 Env and the VLIB Env
	call	tinit	;init terminal
	LXI	H,0	;GET PTR TO CP/M STACK
	DAD	SP	;HL=SP
	SHLD	DUTSTK	;SAVE IT
;
	call	codend	;get free space
	lxi	d,100h	;open area
	dad	d	;large stack area
	shld	savbuf
	dad	d	;100H for SAVBUF
	push	h	;save ptr
	mvi	m,126	;allow 126-char input line
	inx	h
	inx	h	;ptr to INBUF
	shld	inbuf
	pop	h	;pt to beginning
	lxi	d,400h	;large area for expansion
	dad	d
	shld	pinbuf	;ptr to PINBUF
	dad	d
	shld	ctemp	;ptr to CTEMP
	dad	d
	shld	ctempx	;ptr to CTEMPX
	shld	mtabl	;ptr to MACRO TABLE
	lxi	d,100h*10	;10 macros
	dad	d
	shld	gbuff	;group save buffer
	shld	direct	;directory load buffer
;
	lhld	savbuf	;top of stack
	sphl		; SET STACK
;
	call	getspeed
	sta	clock	;set clock speed
	call	getcrt	;get CRT data
	inx	h	;pt to screen size
	mov	a,m	;get it
	sta	pagsiz	;set page size
	call	getmdisk	;get max disk
	sta	mdisk	;and set it
	call	getmuser	;get max user
	sta	muser	;and set it
;
;Set up local jumps to BIOS
;
START1:
	LHLD	BASE+1	;WARM BOOT POINTER
	LXI	D,3	;READY FOR ADD
	DAD	D	
	SHLD	VCONST+1	;CON: Status
	DAD	D
	SHLD	VCONIN+1	;CON: Input
	DAD	D
	SHLD	VCONOT+1	;CON: Output
	DAD	D
	SHLD	VLIST+1		;LST: Output
	DAD	D	;Skip PUNCH
	DAD	D	;Skip RDR
	DAD	D
	SHLD	VHOME+1		;Home Disk
	DAD	D
	SHLD	VSELDK+1	;Select Disk
	DAD	D
	SHLD	VSETRK+1	;Set Track
	DAD	D
	SHLD	VSTSEC+1	;Set Sector
	DAD	D
	SHLD	SETDMA+1	;Set DMA Address
	DAD	D
	SHLD	VREAD+1		;Read Block From Disk
	DAD	D
	SHLD	VWRITE+1	;Write Block To Disk
	DAD	D	;Skip LISTST
	DAD	D
	SHLD	VSCTRN+1	;CP/M 2.x Sector Translation Table
;	JMP	HELLO
;
;Initialization Complete -- Print Signon Message and Begin Command Processing
;
HELLO:
	CALL	GETSTP	;SET UP CP/M PARAMETERS
	CALL	INITP	;INITIALIZE BUFFER PARAMETERS
	CALL	ILPRT
	DB	'DU3 - Disk Utility III, Version '
	DB	VERS/10+'0','.',(VERS MOD 10)+'0'
	DB	CR,LF,CR,LF
	DB	DIM,'Type ? for Help',BRIGHT
	DB	CR,LF,0
;
;Clear Editor Reference
;
	XRA	A
	STA	EDRUN	;EDITOR NOT RUNNING
;
;Save initial command line in INBUF
;
	LXI	H,TBUFF	;PT TO COMMAND LINE BUFFER
	MOV	A,M	;GET CHAR COUNTER
	INX	H	;PT TO FIRST CHAR
	ADD	L	;COMPUTE LOCATION OF AFTER LAST CHAR
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	MVI	M,CR	;SET ENDING CR
	LHLD	INBUF	;PT TO BUFFER
	XCHG		;... IN DE
	LXI	H,TBUFF+1	;PT TO INPUT LINE
	MVI	B,128	;COPY BUFFER
	CALL	MOVE
;
;Establish Initial Position
;
	LXI	D,0	;GROUP 0
	CALL	DEGROUP	;POSITION TO GROUP
	CALL	INQSUB	;PRINT POSITION
;
;Check for initial command
;
	LHLD	INBUF	;INPUT BUFFER
	MOV	A,M
	CPI	CR
	JZ	PRMPTR	;NO INITIAL COMMAND FROM COMMAND LINE
	INX	H	;PT TO FIRST CHAR
;
;Got initial command, set it up
;
	MOV	A,M	;GET FIRST CHAR
	CPI	'/'	;IF SLASH, PRINT INITIAL HELP (TOOLSET CONVENTION)
	JZ	IHELP	;PRINT INITIAL HELP INFO
	XRA	A
	STA	IHFLG	;SET NO INITIAL HELP
	JMP	PRMPTI	;PROCESS AS THOUGH COMMAND LINE WAS TYPED
;
;Input Command Line From User at Console
;
PRMPTR:
	XRA	A	;A=0
	STA	IHFLG	;Set No Initial Help
	LDA	EDRUN	;Check for Editor Running
	ORA	A
	JNZ	EDIT0	;Reenter Editor
	CALL	SINBUF	;Save old INBUF into PINBUF
PRMPTE:
	CALL	RDBUF	;Read Input Line
	CALL	EXMAC	;Expand Macros
;
;Begin Processing Command Line in INBUF
;  At this point, HL points to next character to process
;
PRMPTI:
	MVI	A,0FFH	;SET INFINITE LOOP COUNT
	STA	TOGO	;LOOP COUNT FOR MULTIPLE LOOPS
	STA	TOGO+1
;
;Minor Command Loop; This is the entry point for each individual command in
;  a Command Line; Commands may be separated by semicolons in this manner
;
PROMPT	EQU	$
SETSTK:
	SHLD	STKSAV	;SAVE HL FOR STACK LOAD
	LHLD	SAVBUF	;RESET STACK
	SPHL
	LHLD	STKSAV	;RESTORE HL
	XRA	A	;ZERO 2-UP PRINT FOR DUAL-COLUMN PRINT
	STA	TWOUP	;..SWITCH
	MVI	A,1
	STA	FTSW	;TELL SEARCH NOT TO INCR
	PUSH	H
	LXI	H,TBUFF	;SET NO-READ INPUT BUFFER ADDRESS
	SHLD	BUFAD	;FOR RDBYTE
	POP	H
	CALL	CTLCS	;ABORT?
	JZ	PRMPTR	;..YES, READ BUFFER
;
;Do we have to position in directory after find?
;
	LDA	FINDFL
	ORA	A
	JNZ	POSDIR	;POSITION IN DIRECTORY
;
;Begin Command Evaluation -- Check for EOL and Capitalize
;
	MOV	A,M	;GET NEXT CHAR IN COMMAND LINE
	INX	H	;POINT TO FOLLOWING CHAR
	CPI	CR	;END OF LINE PHYSICALLY?
	JZ	PRMPTR	;INPUT NEW COMMAND LINE IF SO
	CPI	EOLCH	;END OF LINE LOGICALLY?
	JZ	PROMPT	;PROCESS NEXT ELEMENT IF SO
	CALL	UPCASE	;CAPITALIZE COMMAND
	STA	DUMTYP	;TYPE OF DUMP (A,D,H)
	LXI	D,CMDTBL	;PT TO COMMAND TABLE
	CALL	CMD	;PROCESS
	JMP	WHAT	;ERROR RETURN
;
;Command dispatcher
;  If command not found, abort with error message
;  If command file, process command with HL pting to next command char and
;    A containing command letter
;
CMD:
	PUSH	H	;SAVE HL
	MOV	B,A	;COMMAND IN B
	XCHG		;HL PTS TO COMMAND TABLE
CMDLP:
	MOV	A,M	;GET COMMAND
	ORA	A	;0=END OF TABLE
	JZ	CMDER
	CMP	B	;COMPARE COMMAND
	JZ	CMDGO
	INX	H	;PT TO ADR
	INX	H
	INX	H	;PT TO NEXT CMND
	JMP	CMDLP
CMDGO:
	INX	H	;PT TO ADDRESS LOW
	MOV	E,M
	INX	H	;PT TO ADDRESS HIGH
	MOV	D,M
	POP	H	;RESTORE HL
	POP	PSW	;CLEAR RETURN ADDRESS
	MOV	A,B	;COMMAND BACK INTO A
	PUSH	D	;PLACE ADDRESS ON STACK
	RET		;"RUN COMMAND"
CMDER:
	POP	H	;RESTORE HL
	MOV	A,B	;RESTORE COMMAND CHAR IN CASE CMD RUN
	RET		;... IMMEDIATELY AGAIN ON A NEW TABLE
;
;Macro Expansion Routine -- Expand Macros
;
EXMAC:
	LHLD	CTEMP	;BUILD INTO TEMPORARY BUFFER
	XCHG
	LHLD	INBUF	;PT TO INPUT LINE
EXMAC1:
	MOV	A,M	;GET CHAR
	CPI	'0'	;SKIP IF LESS THAN '0'
	JC	EXMAC2
	CPI	'9'+1	;CHECK FOR RANGE
	JNC	EXMAC2
	INX	H	;PT TO NEXT CHAR
	PUSH	H	;SAVE PTR TO NEXT CHAR IN LINE
	SUI	'0'	;CONVERT TO BINARY (0-9)
	MOV	B,A	;RESULT IN B
	MVI	C,0
	LHLD	MTABL	;PT TO BASE OF MACROS
	DAD	B	;PT TO MACRO
	CALL	COPYM	;COPY MACRO INTO LINE
	DCX	D	;BACK UP OVER <CR>
	POP	H	;GET PTR TO NEXT CHAR IN COMMAND LINE
EXMAC2:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CALL	MTEST	;TEST FOR END OF BUFFER
	CPI	CR	;DONE?
	JZ	EXMAC3
	CPI	EOLCH	;LOGICAL EOL?
	JNZ	EXMAC2
	JMP	EXMAC1	;PROCESS NEXT COMMAND
EXMAC3:
	LHLD	CTEMP	;COPY COMMAND LINE BACK
	XCHG
	LHLD	INBUF	;INTO INBUF
	XCHG
	CALL	COPYCR	;COPY TO <CR>
	LHLD	INBUF	;PT TO INBUF
	RET		;EXPANSION COMPLETE
;
;Copy Macro Into Command Line Buffer
;
COPYM:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CALL	MTEST	;CHECK FOR LIMIT
	CPI	CR	;END OF MACRO?
	JNZ	COPYM
	RET
;
;Test for Buffer Full
;
MTEST:
	PUSH	H	;SAVE HL
	PUSH	PSW	;SAVE A
	LHLD	CTEMPX	;CHECK FOR END OF BUFFER
	MOV	A,H	;GET PAGE
	CMP	D	;CHECK PAGE
	JZ	MACERR
	POP	PSW	;GET A
	POP	H	;GET HL
	RET
;
;Macro Command Expansion Error
;
MACERR:
	CALL	ILPRT
	DB	CR,LF,'Error -- Macro Expanded Command Line too Long',0
	JMP	PRMPTR	;NEW COMMAND
;
;Save INBUF into PINBUF for later processing by '@' command
;
SINBUF:
	LHLD	PINBUF		;PT TO PINBUF (PREVIOUS INBUF)
	XCHG
	LHLD	INBUF		;PT TO INBUF
;
;Copy (HL) to (DE) until <CR> Encountered
;
COPYCR:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CPI	CR	;DONE?
	JNZ	COPYCR
	RET

;
;Command Not Found Error
;
WHAT:
	POP	H	; RESTORE HL
	CALL	ILPRT
	DB	DIM,'Invalid Command at or after ',BRIGHT,0
	MOV	A,B	;GET COMMAND LETTER
	CALL	TYPE	;PRINT IT
	JMP	PRMPTR
;
;Memory full error
;
MEMFUL:
	CALL	ILPRT
	DB	'+++ Out of memory +++'
	DB	CR,LF,0
	JMP	PRMPTR
;
;COMMAND:  E
;Edit Current Block
;
EROW	EQU	6	;FIRST ROW OF EDITOR DISPLAY
ECOL	EQU	4	;FIRST COL OF EDITOR DISPLAY
ECOLC	EQU	ECOL+16*3+2	;FIRST COL OF EDITOR CHAR DISPLAY
ECURS	EQU	'>'	;EDITOR CURSOR
;
EDIT:
	CALL	SINBUF	;SAVE COMMAND LINE AS PREVIOUS
	MVI	A,0FFH
	STA	EDRUN	;EDITOR IS RUNNING
;
; SET UP ARROW KEYS
;
	LHLD	ENVPTR	;PT TO ENVIRONMENT DESCRIPTOR
	LXI	D,80H+10H	;PT TO ARROW KEY INFO
	DAD	D
	LXI	D,EDCURT	;PT TO CURSOR TABLE
	MVI	B,4	;4 ARROW KEYS
EDITA:
	MOV	A,M	;GET CHAR
	STAX	D	;STORE CHAR
	INX	H	;PT TO NEXT
	INX	D	;PT TO NEXT ENTRY
	INX	D
	INX	D
	DJNZ	EDITA	;COUNT DOWN
;
; REENTER EDIT WITH PTRS RESET
; REFRESH EDIT SCREEN
;
EDIT0:
	CALL	CLS	;NEW SCREEN
	CALL	AT
	DB	2,32	;ROW 2, COL 32
	CALL	ILPRT	;BANNER
	DB	'DU3 Block Editor',0
	MVI	H,EROW+9	;POSITION FOR COMMAND DISPLAY
	MVI	L,1
	CALL	GOTOXY	;POSITION CURSOR
	CALL	ILPRT	;PRINT COMMAND SUMMARY
	DB	' -- Movement --'
	DB	'  -------------- Operation ---------------',CR,LF
	DB	'      ^E          '
	DB	DIM,'Enter: ',BRIGHT,'A',DIM,' ASCII Chars',BRIGHT
	DB	'      +',DIM,' Next Sector',BRIGHT,CR,LF
	DB	'       ^          '
	DB	DIM,'       ',BRIGHT,'H',DIM,' Hex Numbers',BRIGHT
	DB	'      -',DIM,' Last Sector',BRIGHT,CR,LF
	DB	'  ^S <-+-> ^D    '
	DB	'                     '
	DB	'     ^C',DIM,' Exit DU3   ',BRIGHT
	DB	CR,LF
	DB	'       v          '
	DB	'C',DIM,' DU3 Command Line  ',BRIGHT
	DB	'     ^R',DIM,' Rescreen   ',BRIGHT,CR,LF
	DB	'      ^X          '
	DB	'X',DIM,' Exit Editor to DU3',BRIGHT
	DB	'     ^W',DIM,' Write Block',BRIGHT
	DB	0
	CALL	AT
	DB	2,65
	CALL	ILPRT
	DB	DIM,'Position:',BRIGHT,0
;	JMP	EDITCMD
;
; REFRESH SCREEN DISPLAY DATA ONLY
;
EDITR:
	XRA	A	;A=0
	STA	EINDEX	;SET INDEX TO 0 (FIRST ELEMENT)
	STA	EDERR	;SET NO PREVIOUS ERROR
	CALL	AT	;POSITION CURSOR
	DB	EROW-2,ECOL
	CALL	INQSUB	;PRINT POSITION DATA
	CALL	EDPLOT	;PLOT BUFFER DATA
;
; INPUT EDITOR COMMAND
;
EDITCMD:
	CALL	EDERCL	;CLEAR EDITOR INVALID COMMAND MESSAGE
EDITCMD1:
	CALL	AT	;POSITION FOR COMMAND LINE
	DB	22,10
	CALL	ILPRT
	DB	DIM,'Edit Command?  ',BRIGHT,BS,0
	CALL	CONIN	;GET CHAR
	CALL	UPCASE	;CAPITALIZE
	MOV	B,A	;COMMAND IN B
	LXI	D,EDCURT	;PROCESS CURSOR COMMANDS FIRST
	CALL	CMD	;PROCESS COMMAND
	LXI	D,ECMDTBL	;EDITOR COMMAND TABLE
	CALL	CMD	;PROCESS COMMAND
	MVI	A,0FFH	;SET ERROR FLAG
	STA	EDERR
	CALL	AT	;CLEAR ERROR MESSAGE
	DB	23,15
	CALL	ILPRT
	DB	'Invalid Command',0
	JMP	EDITCMD1
;
;Clear Editor Invalid Command Message
;
EDERCL:
	LDA	EDERR	;PREVIOUS ERROR?
	ORA	A	;0=NO
	RZ
	XRA	A	;CLEAR FLAG
	STA	EDERR
	CALL	AT	;CLEAR ERROR MESSAGE
	DB	23,15
	CALL	ILPRT
	DB	'                         ',0
	RET
;
;PLOT BUFFER DATA
;
EDPLOT:
	MVI	H,EROW	;SET ROW
	MVI	L,ECOL	;SET COLUMN
	CALL	GOTOXY	;POSITION CURSOR
	XCHG		;POSITION IN DE
	LXI	H,TBUFF	;PT TO DATA
	MVI	B,8	;8 LINES
EDIT00:
	MVI	C,16	;16 ELEMENTS
EDIT01:
	MOV	A,M	;GET BYTE
	CALL	HEX	;PRINT AS HEX
	CALL	SPACE	;PRINT 1 SPACE
	INX	H	;PT TO NEXT
	DCR	C	;COUNT DOWN
	JNZ	EDIT01
	XCHG		;POSITION AGAIN
	INR	H	;NEXT ROW
	CALL	GOTOXY
	XCHG
	DCR	B	;COUNT DOWN
	JNZ	EDIT00
	MVI	H,EROW	;RESET ROW
	MVI	L,ECOLC	;RESET COL
	CALL	GOTOXY	;POSITION CURSOR
	XCHG		;POSITION IN DE
	LXI	H,TBUFF	;PT TO DATA
	MVI	B,8	;8 LINES
EDIT02:
	CALL	ASTER	;PRINT BAR
	MVI	C,16	;16 ELEMENTS
EDIT03:
	MOV	A,M	;GET BYTE
	ANI	7FH	;MASK MSB
	CPI	7FH	;7FH IS DOT
	JZ	EDIT7F
	CPI	' '	;SPACE OR MORE?
	JNC	EDIT04
EDIT7F:
	MVI	A,'.'	;PRINT DOT
EDIT04:
	CALL	TYPE	;PRINT BYTE
	INX	H	;PT TO NEXT
	DCR	C	;COUNT DOWN
	JNZ	EDIT03
	CALL	ASTER	;PRINT ENDING BAR
	XCHG		;POSITION AGAIN
	INR	H	;NEXT ROW
	CALL	GOTOXY
	XCHG
	DCR	B	;COUNT DOWN
	JNZ	EDIT02
	CALL	EDCUR	;POSITION CURSOR
	RET
;
;EDITOR COMMAND TABLE
;
ECMDTBL:
	DB	CR	;NOP
	DW	EDITCMD
	DB	'C'-'@'	;^C = EXIT DU3
	DW	EDCC
	DB	'R'-'@'	;^R = REFRESH
	DW	EDIT0
	DB	'E'-'@'	;^E=UP
	DW	EDUP
	DB	'X'-'@'	;^X=DOWN
	DW	EDDOWN
	DB	'D'-'@'	;^D=RIGHT
	DW	EDRIGHT
	DB	'S'-'@'	;^S=LEFT
	DW	EDLEFT
	DB	'W'-'@'	;WRITE BLOCK
	DW	EDITWR
	DB	' '	;NOP
	DW	EDITCMD
	DB	'+'	;ADVANCE
	DW	EDITPLUS
	DB	'-'	;BACKUP
	DW	EDITMINUS
	DB	'A'	;CHANGE ALPHA
	DW	EDITALP
	DB	'C'	;COMMAND LINE
	DW	EDITCL
	DB	'H'	;CHANGE HEX
	DW	EDITHEX
	DB	'X'	;EXIT
	DW	EDITX
	DB	0	;END OF TABLE
;
;  ARROW KEY DEFINITONS FROM TCAP
;
EDCURT:
	DB	0	;0 INDICATES NO ARROW KEYS
	DW	EDUP
	DB	0
	DW	EDDOWN
	DB	0
	DW	EDRIGHT
	DB	0
	DW	EDLEFT
	DB	0	;END OF TABLE
;
;EDITOR BUFFERS
;
EINDEX:
	DS	1	;INDEX ENTRY
EDERR:
	DS	1	;ERROR FLAG
EDRUN:
	DS	1	;FLAG SAYING THAT EDITOR IS RUNNING
;
;Write Block to Disk
;
EDITWR:
	CALL	EDERCL	;CLEAR ERROR LINE
	CALL	WRITE	;WRITE BLOCK
	CALL	AT
	DB	23,15	;MESSAGE
	CALL	ILPRT
	DB	'Block Written',0
	MVI	A,0FFH	;SET ERROR
	STA	EDERR
	JMP	EDITCMD1
;
;Enter ASCII Chars
;
EDITALP:
	CALL	EDERCL	;CLEAR ERROR LINE
	CALL	AT
	DB	22,35
	CALL	ILPRT
	DB	DIM,'Enter Text (<hh> for Hex)',BRIGHT
	DB	CR,LF,' --> ',0
	CALL	RDBUF1	;INPUT TEXT WITHOUT PROMPT
	CALL	EDPRCL	;CLEAR PROMPT LINE
	LDA	EINDEX	;PT TO POSITION
	LXI	D,TBUFF	;COMPUTE OFFSET
	ADD	E
	MOV	E,A
	MOV	A,D
	ACI	0
	MOV	D,A	;DE PTS TO BYTE, HL PTS TO TEXT
EDITA1:
	MOV	A,M	;GET CHAR
	CPI	CR	;EOL?
	JZ	EDITA2	;REFRESH SCREEN
	CALL	GETVAL	;GET ASCII OR <HEX> VALUE
	STAX	D	;UPDATE BYTE
	INX	H	;PT TO NEXT INPUT CHAR
	INR	E	;PT TO NEXT BUFFER BYTE
	JNZ	EDITA1
EDITA2:
	CALL	EDPLOT	;REPLOT
	JMP	EDITCMD1	;DONE-REFRESH SCREEN
;
;Enter Numbers
;
EDITHEX:
	CALL	EDERCL	;CLEAR ERROR LINE
	CALL	AT
	DB	22,35
	CALL	ILPRT
	DB	DIM,'Enter Hex Numbers (#nn for Dec)'
	DB	BRIGHT
	DB	CR,LF,' --> ',0
	CALL	RDBUF1	;INPUT TEXT WITHOUT PROMPT
	CALL	EDPRCL	;CLEAR PROMPT LINE
	LDA	EINDEX	;PT TO POSITION
	LXI	D,TBUFF	;COMPUTE OFFSET
	ADD	E
	MOV	E,A
	MOV	A,D
	ACI	0
	MOV	D,A	;DE PTS TO BYTE, HL PTS TO TEXT
EDITH1:
	MOV	A,M	;GET HEX DIGIT
	CPI	CR	;EOL?
	JZ	EDITA2	;REFRESH SCREEN
	CPI	' '	;SKIP SPACES
	JNZ	EDITH2
	INX	H	;SKIP SPACE
	JMP	EDITH1
EDITH2:
	PUSH	D	;SAVE PTR
	CALL	HEXIN	;GET VALUE AND POSITION HL
	MOV	A,E	;... IN A
	POP	D	;GET PTR
	STAX	D	;PUT BYTE
	INR	E	;ADVANCE TO NEXT BYTE
	JNZ	EDITH1
	JMP	EDITA2	;DONE-REFRESH
;
;CLEAR PROMPT LINE
;
EDPRCL:
	CALL	AT	;PROMPT LINE
	DB	22,35
	MVI	B,40	;40 POSITIONS
	CALL	EDPCL
	CALL	AT	;USER INPUT
	DB	23,1
	MVI	B,79	;79 POSITIONS
EDPCL:
	CALL	SPACE	;CLEAR PROMPT LINE WITH SPACES
	DCR	B
	JNZ	EDPCL
	RET
;
;Enter Command Line from Editor
;
EDITCL:
	CALL	EDERCL	;CLEAR ERROR LINE
	CALL	CRLF	;NEW LINE
	JMP	PRMPTE	;GET COMMAND LINE FROM USER
;
;Advance to Next Block
;
EDITPLUS:
	CALL	NXTSEC	;ADVANCE SECTORS
EDITP1:
	PUSH	H
	LHLD	CURSEC
	XCHG
	CALL	SETSEC	;SET SECTOR
	LHLD	CURTRK
	XCHG
	CALL	SETTRK	;SET TRACK
	POP	H
	CALL	READ	;READ IN BLOCK
	CALL	CLCSUB	;CALCULATE GROUP DATA
	JMP	EDITR	;REFRESH DATA
;
;Backup to Last Block
;
EDITMINUS:
	CALL	LSTSEC	;BACKUP BLOCK
	JMP	EDITP1
;
;Exit EDIT Mode
;
EDITX:
	XRA	A
	STA	EDRUN	;EDITOR IS NOT RUNNING NOW
	CALL	EDERCL	;CLEAR ERROR LINE
	JMP	PRMPTR
;
;Exit DU3
;
EDCC:
	CALL	EDERCL	;CLEAR ERROR LINE
	JMP	EXIT
;
;EDIT MOVE: UP
;
EDUP:
	CALL	EDCCUR	;CLEAR CURSOR
	LDA	EINDEX	;BACKUP INDEX BY 16
	SUI	16
;
;Common EDIT MOVE Routine - on input, A=new index
;
EDMOVE:
	ANI	7FH	;MOD 128
	STA	EINDEX
	CALL	EDCUR	;SET CURSOR
	JMP	EDITCMD
;
;EDIT MOVE: DOWN
;
EDDOWN:
	CALL	EDCCUR	;CLEAR CURSOR
	LDA	EINDEX	;INCREMENT INDEX BY 16
	ADI	16
	JMP	EDMOVE	;COMMON ROUTINE
;
;EDIT MOVE: RIGHT
;
EDRIGHT:
	CALL	EDCCUR	;CLEAR CURSOR
	LDA	EINDEX	;INCREMENT INDEX BY 1
	INR	A
	JMP	EDMOVE	;COMMON ROUTINE
;
;EDIT MOVE: LEFT
;
EDLEFT:
	CALL	EDCCUR	;CLEAR CURSOR
	LDA	EINDEX	;DECREMENT INDEX BY 1
	DCR	A
	JMP	EDMOVE	;COMMON ROUTINE
;
;EDIT SUBROUTINE: EDCUR
; Position Editor Cursor at EINDEX
;EDIT SUBROUTINE: EDCCUR
; Clear Editor Cursor at EINDEX
;
EDCUR:
	PUSH	H	;SAVE HL
	MVI	C,ECURS	;CURSOR CHAR
	CALL	EDSETCUR
	CALL	AT	;UPDATE DATA
	DB	2,75
	LDA	EINDEX	;PT TO BYTE AT CURSOR
	LXI	H,TBUFF
	ADD	L
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A	;HL PTS TO BYTE AT CURSOR
	MOV	A,M	;GET BYTE
	CALL	HEX	;PRINT AS HEX
	CALL	SPACE
	MOV	A,M	;GET BYTE
	POP	H	;RESTORE HL
	ANI	7FH	;MASK
	CPI	7FH	;7FH IS DOT
	JZ	EDC7F
	CPI	' '	;OUTPUT CHAR OR DOT
	JNC	TYPE
EDC7F:
	MVI	A,'.'	;DOT
	JMP	TYPE
EDCCUR:
	MVI	C,' '	;CLEAR CURSOR
EDSETCUR:
	CALL	EDROW	;COMPUTE ROW
	ANI	0FH	;COMPUTE COL MOD 16
	MOV	B,A	;RESULT IN B
	ADD	A	;*2
	ADD	B	;*3
	ADI	ECOL	;ADD IN COL
	DCR	A	;SUBTRACT 1
	MOV	L,A	;COL POSITION SET
	CALL	GOTOXY	;POSITION CURSOR
	MOV	A,C	;OUTPUT CHAR
	JMP	TYPE
;
;Compute Row from EINDEX
;
EDROW:
	LDA	EINDEX	;GET INDEX
	MOV	B,A	;SAVE IN B
	RRC		;DIVIDE BY 16
	RRC
	RRC
	RRC
	ANI	0FH	;MASK FOR LSB ONLY
	ADI	EROW	;COMPUTE ROW
	MOV	H,A	;ROW SET
	MOV	A,B	;GET INDEX
	RET
;
;COMMAND:  @
;Repeat Previous Command Line
;
PCMD:
	MOV	A,M	;GET NEXT CHAR
	CPI	CR	;SHOULD BE <CR>
	JZ	PCMD1
	CALL	ILPRT
	DB	CR,LF,'Warning:  Remainder of Command Line after "@" Deleted',0
PCMD1:
	CALL	ILPRT
	DB	CR,LF,DIM,'Command --',BRIGHT,CR,LF,0
	LHLD	INBUF	;COPY INTO INBUF
	XCHG
	LHLD	PINBUF	;GET PREVIOUS COMMAND
PCMD2:
	MOV	A,M	;GET CHAR
	STAX	D	;PUT CHAR
	INX	H	;PT TO NEXT
	INX	D
	CPI	CR	;END OF LINE?
	PUSH	PSW	;SAVE FLAG
	CALL	TYPE	;PRINT CHAR
	POP	PSW	;GET FLAG
	JNZ	PCMD2
	MVI	A,LF	;<LF>
	CALL	TYPE
	LHLD	INBUF	;RESTART COMMAND PROCESSING
	JMP	PRMPTI	;INCLUDE LOOP CAPABILITY
;
;COMMAND:  :
;Define or Print Macro
;:n<text> Defines Macro n, 0<=n<=9; ::n Prints Macro n, 0<=n<=9
;
MAC:
	MOV	A,M	;GET NEXT CHAR
	CALL	UPCASE	;CAPITALIZE
	CPI	'P'	;PRINT MACRO?
	JNZ	MACROD	;IF NOT, DEFINE MACRO
	INX	H	;PT TO MACRO NUMBER
	MOV	A,M	;GET IT
	CALL	UPCASE	;CAPITALIZE
	CPI	'@'	;PRINT PREVIOUS COMMAND?
	JZ	PCPR
	PUSH	PSW	;SAVE A
	call	cls
	cz	crlf
	CALL	ILPRT
	DB	DIM,'Macro Definitions --',BRIGHT,0
	POP	PSW	;GET A
	CPI	'A'	;PRINT ALL MACROS?
	JZ	AMACPR
	CALL	MNUM	;CHECK FOR VALID NUMBER AND RETURN # IN D
	INX	H	;PT TO CHAR AFTER MACRO NUMBER
	CALL	MACPR	;PRINT MACRO WHOSE NUMBER IS IN D
	JMP	PROMPT
;
;Print Previous Command
;
PCPR:
	INX	H	;PT TO CHAR AFTER '@'
	LXI	D,PROMPT	;SET UP RET ADR
	PUSH	D	;RETURN ADR ON STACK
	PUSH	H	;SAVE PTR
	CALL	ILPRT
	DB	DIM,'Previous Command Line Definition --',BRIGHT
	DB	CR,LF,'@: ',0
	LHLD	PINBUF	;PT TO PREVIOUS COMMAND
	JMP	MPRINT		;USE MACRO PRINT FACILITY
;
;Print All Macros
;
AMACPR:
	INX	H	;PT TO CHAR AFTER 'A'
	MVI	D,0	;SET FOR FIRST MACRO
AMPRL:
	CALL	MACPR	;PRINT MACRO WHOSE NUMBER IS IN D
	INR	D	;INCREMENT MACRO NUMBER
	MOV	A,D	;GET VALUE
	CPI	10	;DONE?
	JNZ	AMPRL
	JMP	PROMPT	;CONTINUE PROCESSING
;
;Print Macro Whose Number (0-9) is in D
;
MACPR:
	PUSH	H	;SAVE PTR
	CALL	ILPRT	;PRINT HEADER
	DB	CR,LF,DIM,0
	MOV	A,D	;GET NUMBER
	ADI	'0'	;CONVERT TO ASCII
	CALL	TYPE	;PRINT
	CALL	ILPRT
	DB	': ',BRIGHT,0
	LHLD	MTABL	;PT TO TABLE OF MACROS
	MVI	E,0	;PAGE OFFSET OF ZERO; MACRO NUMBER ALREADY IN D
	DAD	D	;PT TO MACRO
MPRINT:
	MOV	A,M	;GET CHAR
	INX	H	;PT TO NEXT
	CPI	CR	;END OF MACRO?
	PUSH	PSW	;SAVE FLAG
	CALL	TYPE	;PRINT CHAR
	POP	PSW	;GET FLAG
	JNZ	MPRINT
	MVI	A,LF	;<LF>
	CALL	TYPE
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;Check char in A for valid Macro Number (0-9), print error message if
; not, return number in D if so
;
MNUM:
	SUI	'0'	;CONVERT TO 0-9
	JC	MNERR	;ERROR IF LESS
	CPI	10	;RANGE?
	JNC	MNERR
	MOV	D,A	;RESULT IN D
	RET
MNERR:
	CALL	ILPRT
	DB	CR,LF,'Invalid Macro Number Specified in Command',0
	JMP	PRMPTR	;NEW COMMAND
;
;Define Macro
;
MACROD:
	CALL	MNUM	;CHECK NUMBER AND RETURN IN D
	INX	H	;PT TO CHAR AFTER MACRO NUMBER
	PUSH	H	;SAVE PTR
	LHLD	MTABL	;PT TO MACRO TABLE
	MVI	E,0	;SET EVEN PAGE
	DAD	D	;PT TO MACRO ENTRY IN HL
	XCHG		;... IN DE
	POP	H	;PT TO MACRO TEXT
	CALL	COPYCR	;COPY TO <CR>
	JMP	PRMPTR	;NEW COMMAND
;
;COMMAND:  !
;Delay for user input
;
UWAIT:
	CALL	WAIT	; USE WAIT ROUTINE
	JMP	PROMPT
;
;COMMAND:  #
;Print disk statistics
;
STATS:
	PUSH	H	;SAVE POINTER TO NEXT COMMAND
	call	cls
	cz	crlf
	CALL	ILPRT
	DB	DIM
	DB	'    -- Queue Information --',BRIGHT,CR,LF
	DB	CR,LF
	DB	0
	CALL	QSTATS	;PRINT STATUS INFO
	CALL	ILPRT
	DB	CR,LF
	DB	DIM
	DB	'    -- Disk Information --',BRIGHT,CR,LF
	DB	CR,LF
	DB	DIM,'Disk Drive:		',BRIGHT,0
	LDA	DRIVE
	ADI	'A'	;CONVERT TO ASCII
	CALL	TYPE	;PRINT DRIVE LETTER
	CALL	ILPRT
	DB	CR,LF,DIM,'Tracks:			',BRIGHT,0
	LHLD	MAXTRK	;PRINT NUMBER OF TRACKS
	INX	H
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,DIM,'Sectors/Track:		',BRIGHT,0
	LHLD	SPT	;PRINT NUMBER OF SECTORS/TRACK
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,DIM,'Group Size:		',BRIGHT,0
	LDA	BLM	;PRINT SIZE OF A GROUP
	INR	A
	MOV	L,A
	MVI	H,0
	CALL	DEC
	CALL	ILPRT
	DB	DIM,' Blocks/Group',BRIGHT
	DB	CR,LF,DIM,'Total Groups:		',BRIGHT,0
	LHLD	DSM	;PRINT TOTAL NUMBER OF GROUPS ON A DISK
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,DIM,'Directory Entries:	',BRIGHT,0
	LHLD	DRM	;PRINT NUMBER OF DIRECTORY ENTRIES
	INX	H
	CALL	DEC
	CALL	ILPRT
	DB	CR,LF,DIM,'System Tracks:		',BRIGHT,0
	LHLD	SYSTRK	;PRINT NUMBER OF SYSTEM TRACKS
	CALL	DEC
	CALL	SWAIT
	POP	H	;RESTORE POINTER TO NEXT COMMAND
	JMP	PROMPT
;
;COMMAND:  N
;The following command resets the disk
;system thru CP/M, and may be usable for
;changing the disk density or format.
;This can only be done if your BIOS resets
;the auto-density select parameters at
;every track-zero access.
;
NEWDSK:
	PUSH	H	;SAVE POINTER TO NEXT LETTER
	MVI	C,RESETDK	;BDOS RESET DISK FUNCTION
	CALL	BDOS
	LDA	DRIVE	;RESELECT CURRENT DRIVE
	MOV	C,A
	POP	H
	CALL	SELECT
	JMP	PROMPT
;
;COMMAND:  Q
;Queue Control
;
QUEUER:
	MOV	A,M	;GET 2ND ARGUMENT
	CALL	UPCASE	;CAPITALIZE
	CPI	EOLCH	;END OF LINE?
	JZ	QSTAT	;STATUS REPORT
	CPI	CR	;END OF LINE?
	JZ	QSTAT
	INX	H	;PT TO AFTER KEY CHAR
	PUSH	H	;SAVE PTR
	CPI	'Z'	;ZERO QUEUE?
	JZ	QZERO
	CPI	'S'	;SAVE QUEUE?
	JZ	QFSAVE
	POP	H	;GET PTR
	CALL	ILPRT
	DB	'Invalid Queue Command',CR,LF,0
	JMP	PRMPTR	;ABORT LINE ON ERROR
;
;  Zero the Queue
;
QZERO:
	LHLD	DIRECT	;ZERO QUEUE
	SHLD	QNXT	;SET NEXT
	SHLD	QLST	;SET LAST
	LXI	H,0	;ZERO COUNT
	SHLD	QCNT
	POP	H	;GET PTR AND FALL THRU TO QSTAT
;
;  Print Status of Queue
;
QSTAT:
	PUSH	H	;SAVE PTR TO NEXT CHAR
	call	cls
	cz	crlf
	CALL	ILPRT
	DB	DIM
	DB	'** Queue Status Summary **'
	DB	BRIGHT
	DB	CR,LF,0
	CALL	QSTATS	;PRINT STATUS
	POP	H	;RESTORE PTR
	JMP	PROMPT
QSTATS:
	LHLD	QCNT	;GET SIZE OF QUEUE
	CALL	PRQCNT	;PRINT DATA
	CALL	PRQSPAC	;PRINT SPACE AVAILABLE INFO
	CALL	ILPRT
	DB	CR,LF
	DB	DIM,'Group Save Buffer Address:  ',BRIGHT,0
	PUSH	H
	LHLD	GBUFF	;BC=ADDRESS
	MOV	B,H
	MOV	C,L
	POP	H
	CALL	HEXB1	;PRINT AS HEX
	CALL	ILPRT
	DB	' Hex',CR,LF
	DB	0
	CALL	ILPRT
	DB	DIM,'Address of Head of Queue:   ',BRIGHT,0
	LHLD	QNXT	;PRINT ADDRESS OF HEAD OF QUEUE
	MOV	B,H	;... ADDRESS IN BC
	MOV	C,L
	CALL	HEXB1	;PRINT IN HEX
	CALL	ILPRT
	DB	' Hex',CR,LF
	DB	DIM,'Address of Tail of Queue:   ',BRIGHT,0
	LHLD	QLST	;PRINT ADDRESS OF TAIL OF QUEUE
	MOV	B,H
	MOV	C,L
	CALL	HEXB1
	CALL	ILPRT
	DB	' Hex',CR,LF,0
	RET
;
;  Print Amount of Space Left in Queue
;
PRQSPAC:
	LXI	B,-1	;SET COUNT
	LHLD	QLST	;GET PTR TO QUEUE TAIL
QSTAT1:
	INX	B	;INCREMENT COUNT
	LXI	D,80H	;PT TO NEXT QUEUE ELEMENT
	DAD	D
	XCHG		;WRAP AROUND
	CALL	QWRAP
	LHLD	QNXT	;GET PTR TO FIRST ELEMENT
	XCHG
	MOV	A,H	;COMPARE
	CMP	D
	JNZ	QSTAT1
	MOV	A,L
	CMP	E
	JNZ	QSTAT1
	MOV	H,B	;HL=BLOCK COUNT
	MOV	L,C
	CALL	DEC	;PRINT AS DECIMAL
	CALL	ILPRT
	DB	DIM,'	Blocks Left in Queue',BRIGHT,CR,LF,0
	RET
;
;  Save Queue as a File
;
QFSAVE:
	MOV	A,M	;GET FIRST CHAR OF FILE NAME
	CPI	EOLCH	;EOL?
	JZ	WHAT
	CPI	CR	;EOL?
	JZ	WHAT
	LXI	D,FCB	;START TO FILL FCB
	XRA	A	;A=0
	STAX	D	;SELECT DEFAULT DRIVE
	INX	D	;PT TO FILE NAME
	MVI	B,8	;SAVE FILE NAME
	CALL	MVNAME
	MVI	B,3	;SAVE FILE TYPE
	CALL	MVNAME
	PUSH	H	;SAVE PTR TO NEXT CHAR
	LHLD	QCNT	;ANY ELEMENTS IN QUEUE?
	MOV	A,H
	ORA	L
	JZ	QEMPTY
	PUSH	H	;SAVE QUEUE COUNT
	CALL	NORITE	;CAN'T WRITE NOW
	LXI	D,FCB	;PT TO FCB
	CALL	FCBINIT	;INIT FCB
	MVI	C,DELF	;DELETE FILE
	PUSH	D	;SAVE DE
	CALL	BDOS
	POP	D
	CALL	FCBINIT	;INIT FCB AGAIN
	MVI	C,MAKEF	;CREATE FILE
	CALL	BDOS
	POP	B	;GET QUEUE COUNT IN BC
	LHLD	QNXT	;PT TO NEXT BLOCK IN QUEUE
QFS1:
	PUSH	B	;SAVE COUNT
	LXI	D,TBUFF	;COPY INTO TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	XCHG		;PT TO NEXT QUEUE BLOCK IN DE
	CALL	QWRAP	;WRAP AROUND
	PUSH	D	;SAVE PTRS
	LXI	D,FCB	;PT TO FCB
	MVI	C,WRITEF	;WRITE BLOCK TO FILE
	CALL	BDOS
	POP	H	;GET PTR TO NEXT BLOCK
	POP	B	;GET COUNT
	DCX	B	;COUNT DOWN
	MOV	A,B	;DONE?
	ORA	C
	JNZ	QFS1
	LXI	D,FCB	;CLOSE FILE
	MVI	C,CLOSEF
	CALL	BDOS
	CALL	ILPRT
	DB	'Queue Saved in File',CR,LF,0
	POP	H	;PT TO NEXT CHAR
	JMP	PROMPT
FCBINIT:
	PUSH	D	;SAVE PTR
	LXI	H,12	;SKIP TO EX FIELD
	DAD	D
	MVI	B,24	;ZERO 36 BYTES
	XRA	A	;A=0
FCBIN1:
	MOV	M,A	;STORE ZEROES
	INX	H
	DJNZ	FCBIN1
	POP	D	;RESTORE PTR
	RET
;
;COMMAND:  *
;Repeat buffer contents
;
REPEAT:
	CALL	DECIN	;NN SPECIFIED?
	MOV	A,D
	ORA	E
	JZ	NNN	;NO -- SET FOR INFINITE LOOP OR SIMPLE REPEAT
	LHLD	TOGO	;LOAD LOOP FLAG
	INX	H	;TEST FOR FIRST TIME
	MOV	A,H
	ORA	L	;WAS IT 0FFFFH?; IF SO, WE HAVE NEW VALUE
	JNZ	NNN	;NO: COUNTING
	XCHG		;GET COUNT
	SHLD	TOGO	;SET COUNT
;
NNN:
	LHLD	TOGO	;GET CURRENT COUNT
	XCHG		;DE=CURRENT COUNT, HL=COUNT LIMIT
	LHLD	INBUF	;PT TO FIRST CHAR FOR REPEAT
	INX	D	;TEST FOR 0FFFFH
	MOV	A,D	;IF 0FFFFH, INX D MADE DE=0
	ORA	E
	JZ	PROMPT	;CONTINOUS LOOP IF 0FFFFH
	DCX	D	;COUNT DOWN
	DCX	D	;MAKE UP FOR PREV INX D
	XCHG
	SHLD	TOGO	;SET NEW COUNT (1 LESS THAN BEFORE)
	MOV	A,H	;ALL DONE?
	ORA	L
	XCHG		;GET BACK INBUF PTR IN HL
	JNZ	PROMPT	;KEEP GOING IF NOT YET ZERO
	JMP	PRMPTR	;ALL DONE
;
;COMMAND:  U
;Set CP/M 2.x user number
;
USER:
	CALL	DECIN	;GET REQUESTED USER NO.
	LDA	MUSER	;GET MAX USER
	MOV	B,A	;... IN B
	MOV	A,E
	CMP	B	;VALID?
	JNC	USRERR
	MOV	A,D	;HIGH-ORDER BYTE MUST BE ZERO FOR VALID NUMBER
	ORA	A
	JNZ	USRERR
	MOV	A,E	;SAVE USER NUMBER
	STA	UNUM
	MVI	C,SUSER	;SET USER NUMBER
	PUSH	H		;SAVE CHAR POINTER
	CALL	BDOS		;SET USER NO.
	POP	H
	JMP	PROMPT
USRERR:
	CALL	ILPRT
	DB	'User Number Out of Range',CR,LF,0
	JMP	PRMPTR
;
;COMMAND:  P
;Toggle print flag
;
PRNTFF:
	LDA	PFLAG	;TOGGLE PRINT FLAG
	XRI	1
	STA	PFLAG
	JMP	PROMPT
;
;COMMAND:  Z
;Sleep routine, in seconds
;
SLEEP:
	CALL	DECIN	;GET COUNT IF ANY
	MOV	A,E	;ANY?
	ORA	A
	JNZ	SLEPLP
	MVI	E,1	; 1 SEC DEFAULT
;
SLEPLP:
	LDA	CLOCK	; GET CLOCK SPEED
	MOV	D,A
;
SLEEP1:
	LXI	B,41700	; APPROX 1 SEC @ 1MHz
;
SLEEP2:
	DCX	B	;COUNT DOWN FOR 1 MHz [5 CYCLES]
	MOV	A,B	;[5 CYCLES] <-- TOTAL TIME: 24 CYCLES
	ORA	C	;[4 CYCLES] <-- (24 MU-SECS AT 1MHz)
	JNZ	SLEEP2	;[10 CYCLES]
	PUSH	D
	CALL	CTLCS	;ABORT?
	POP	D
	JZ	PRMPTR
	DCR	D	;COUNT DOWN FOR CLOCK SPEED
	JNZ	SLEEP1
	DCR	E	;COUNT DOWN NUMBER OF REQUESTED SECONDS
	JNZ	SLEPLP
	JMP	PROMPT
;
;Check for control-C or S
;
CTLCS:
	CALL	CONST	;CHAR AVAILABLE?
	ORA	A
	JNZ	GETC
	ORI	1	;NO CHAR, RETURN NZ
	RET
;
GETC:	CALL	CONIN	;INPUT CHAR
	ANI	1FH	;ALLOW ASCII
	CPI	'S'-40H	;WAIT FOR NEXT CHAR IF ^S OR S OR s
	CZ	CONIN
	CPI	'C'-40H	;CHECK FOR ^C OR C OR c
	RET		;0 SET IF CTL-C
;
;Initialize Memory Buffers
;
INITP:
	XRA	A	;A=0
	STA	HEXAD	;CLEAR ADDRESS
	STA	HEXAD+1
	STA	PFLAG	;SET NO PRINT
	STA	SAVEFL	;SET NO SAVE DONE
	STA	WRFLG	;MAY NOT WRITE
	STA	DIRPOS	;SET NO DIRECTORY POSITION
	STA	FINDFL	;SET NO POSITION
	INR	A	;A=1
	STA	FTSW	;SET SEARCH WITHOUT INCREMENT
	STA	NOTPOS	;NOT POSITIONED
	LXI	H,0	;HL=0
	SHLD	QCNT	;SET NO ELEMENTS IN QUEUE
	SHLD	MFPTR	;SET NO MULTI FILE PTR
	SHLD	CURTRK	;SET TRACK 0
	INX	H	;HL=1
	SHLD	CURSEC	;SET LOGICAL SECTOR 1
	SHLD	PHYSEC	;SET PHYSICAL SECTOR 1
	LHLD	PINBUF	;SET PREVIOUS COMMAND TO NIL
	MVI	M,CR	;CLEAR PREVIOUS COMMAND
	LHLD	DIRECT	;SET FIRST AND LAST QUEUE ELEMENT PTRS
	SHLD	QNXT
	SHLD	QLST
	LHLD	MTABL	;CLEAR MACRO TABLE
	MVI	B,10	;10 ENTRIES
INITP1:
	MVI	M,CR	;STORE <CR>
	INR	H	;PT TO NEXT PAGE
	DJNZ	INITP1
	RET
;
;Set up flags, etc, at initialization
;Find our way at initialization
;
GETSTP:
	PUSH	H
	LHLD	INBUF	;PT TO INPUT BUFFER
	MVI	M,CR	;INITIALIZE INPUT BUFFER
	POP	H
	MVI	C,SUSER	;GET USER NUMBER
	MVI	E,0FFH	;GET USER
	CALL	BDOS
	STA	UNUM	;SET USER NUMBER
	MVI	C,GETDSK
	CALL	BDOS	;GET CURRENT DISK
	MOV	C,A	;WE HAVE TO SELECT
	JMP	SELECT	;TO GET THE DPH
;
;COMMAND:  L
;Log in the selected disk
;
LOGIN:
	CALL	DOLOG
	JMP	PROMPT
;
DOLOG:
	MOV	A,M	;DISK REQUESTED?
	LXI	D,0
	CPI	CR	;NO REQUEST OF PHYSICAL EOL
	JZ	LGNODK
	CPI	EOLCH	;NO REQUEST IF LOGICAL EOL
	JZ	LGNODK
	CALL	UPCASE	;CAPITALIZE
	INX	H	;POINT TO NEXT CHAR
	SUI	'A'	;CONVERT TO 0-15
	MOV	C,A	;DISK NUMBER IN C
	LDA	MDISK	;GET MAX DISK
	MOV	B,A	;... IN B
	MOV	A,C
	CMP	B
	JC	SELECT
	CALL	ILPRT
	DB	'Disk Letter Out of Range',CR,LF,0
	JMP	PRMPTR
;
;Select Disk Whose Number is in C (A=0, B=1, etc)
;
SELECT:
	PUSH	H	;SAVE PTR TO NEXT COMMAND LETTER
	MOV	A,C
	STA	DRIVE	;REMEMBER LATER WHERE WE ARE
;
VSELDK: CALL	$-$	;ADDR FILLED IN BY 'INIT'
	MOV	A,H
	ORA	L
	JZ	WHAT	;SELECT ERROR
	MOV	E,M	;GET THE SECTOR TABLE PNTR
	INX	H
	MOV	D,M
	INX	H
	XCHG
	SHLD	SECTBL	;SET THE SECTOR TABLE PTR
	LXI	H,8	;OFFSET TO DPBPTR
	DAD	D
	MOV	A,M	;PICK UP DPB POINTER
	INX	H	;  TO USE
	MOV	H,M	;  AS PARAMETER
	MOV	L,A	;  TO LOGIT
	CALL	LOGIT
	LHLD	SYSTRK	;RESET TRACK AND SECTOR
	XCHG		;  TO DIRECTORY
	CALL	SETTRK	;  ON EVERY
	LXI	D,1	;  LOGIN
	CALL	SETSEC	;  CHANGE
	LHLD	PHYSEC	;THIS LOGIC WILL TELL
	MOV	A,H	;  IF FIRST SEC
	ORA	L	;  IS PHYSICAL 0
	STA	FIRST0
	CALL	CLCSUB	;CALCULATE WHAT GROUP/GRPDISP WE ARE IN
	POP	H	;GET PTR TO NEXT LETTER
;
LGNODK:
	CALL	NORITE	;SET NO DISK I/O DONE (NO POSITION)
	RET
;
;Read in the disk directory
;
REDDIR:
	PUSH	H	;SAVE PTR TO NEXT LETTER
	CALL	NORITE	;POSITIONING LOST
	LHLD	SYSTRK	;SAVE CURRENT TRACK
	SHLD	CURTRK
	LXI	H,1	;SET SECTOR 1
	SHLD	CURSEC
	LHLD	DRM	;GET DIR SIZE FROM DPB
	INX	H	;MAKE 1-RELATIVE
	CALL	ROTRHL
	CALL	ROTRHL	;DIVIDE BY 4 (4 NAMES/SECTOR)
	MOV	B,H	;BC=NUMBER OF BLOCKS TO READ
	MOV	C,L
	XCHG
	LHLD	DIRECT	;DMA ADDR
	XCHG		;... IN DE
;
;Read Disk Directory Loop
;
RDIRLP:
	PUSH	B	;SAVE REGS
	PUSH	D
	MOV	B,D	;BC=DMA ADDRESS
	MOV	C,E
	LDA	BDOS+2	;CHECK MEM AVAIL
	DCR	A	;ARE WE RNNING INTO BDOS?
	CMP	D
	JC	MEMFUL	;MEMORY FULL ERROR IF SO
	CALL	SETDMA	;SET DMA ADDRESS TO THAT IN BC
	LHLD	CURTRK	;SET TRACK
	XCHG
	CALL	SETTRK
	LHLD	CURSEC	;SET SECTOR
	XCHG
	CALL	SETSEC
	CALL	READ	;READ DIRECTORY BLOCK
	CALL	NXTSEC	;INCREMENT TO NEXT SECTOR
	POP	D
	POP	B
	LXI	H,80H	;ADVANCE TO NEXT DMA ADDRESS
	DAD	D
	XCHG		;DE=NEXT DMA ADDRESS
	DCX	B	;COUNT DOWN DIRECTORY BLOCKS
	MOV	A,B
	ORA	C
	JNZ	RDIRLP
	LXI	B,TBUFF	;RESET DMA ADDRESS TO TBUFF
	CALL	SETDMA
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;COMMAND:  M
;Map the directory
;
MAP:
	PUSH	H	;SAVE PTR
	LHLD	QCNT	;GET COUNT
	MOV	A,H
	ORA	L
	POP	H
	JZ	MAP1	;PROCEED IF QUEUE EMPTY
	CALL	ILPRT	;PRINT ABORT MESSAGE
	DB	CR,LF,'MAP not permitted -- Block Queue would be overlaid',0
	JMP	PRMPTR
MAP1:
	CALL	PAGSET	;SET PAGING COUNTER
	XRA	A
	STA	ONLY1	;SET FLAG FOR ALL GROUPS (NOT ONLY 1)
	CALL	REDDIR	;READ IN DIRECTORY
	MVI	C,0	;INIT START GRP #
	LDA	AL0	;READ DIR GRP BITS
	CALL	COLECT	;COLLECT COUNT OF DIR GRPS..
	LDA	AL1	;..IN REGISTER C
	CALL	COLECT
	MVI	B,0	;BC NOW HAS A DEFAULT START GRP #
	CALL	HEXIN	;GET SPECIFIED GROUP IF ANY
	PUSH	H	;SAVE INBUF PTR
	MOV	A,E	;GET START
	ORA	D	;NOTHING?
	JZ	MAPDF	;..YES, DFLT
	MVI	A,0FFH	;SET FLAG FOR ONLY 1 GROUP
	STA	ONLY1
	MOV	B,D	;GET VALUE IN BC
	MOV	C,E
;
MAPDF:
	CALL	HEXB	;PRINT FIRST GROUP NUMBER
	MVI	A,'-'	;PRINT SEPARATOR
	CALL	TYPE
	MVI	A,' '	;SET NO DUPLICATES
	STA	DUPFLG
	CALL	GETGRP	;GET GRP(C) TO HL
;
MAPCNT:
	INX	B	;NEXT GRP #
	PUSH	H
	LHLD	DSM	;GET HIGHEST GRP #
	INX	H	;PLUS 1 FOR COMPARISON
	MOV	A,L	;WHEN BC REACHES DSM+1..
	CMP	C	;..THEN WE HAVE EXCEEDED..
	JNZ	MAPC1	;..THE DISK CAPACITY..
	MOV	A,H
	CMP	B
;
MAPC1:
	POP	H
	JZ	MAPEND	;..AND WE ARE DONE
	PUSH	H
	CALL	GETGRP	;GET ANOTHER
	POP	D	;SEE IF SAME
	CALL	CTLCS	;ABORT?
	JZ	MAPND2
	MOV	A,D
	CMP	H
	JNZ	MAPDIF
	MOV	A,E
	CMP	L
	JZ	MAPCNT	;SAME, CONTINUE
;
;Different file encountered
;
MAPDIF:
	DCX	B
	CALL	HEXB	;PRINT ENDING GROUP NUMBER
	INX	B
	XCHG
	CALL	MAPNAM	;PRINT FILE NAME
	LDA	ONLY1	;ONLY 1 NAME TO BE PRINTED?
	ORA	A	;0=NO
	JNZ	MAPND1
	JMP	MAPDF
;
;End of map
;
MAPEND:
	DCX	B	;GET LAST
	CALL	HEXB	;PRINT LAST GROUP NUMBER
	CALL	MAPNAM	;PRINT FILE NAME
	CALL	WAIT	;DELAY FOR USER
MAPND1:
	POP	H
	CALL	CRLF	;NEW LINE
;
;End of map - reposition to previous group
;
MAPND2:
	PUSH	H
	LHLD	GROUP	;POINT TO GROUP IN DE
	XCHG
	JMP	POSGP2
;
;Print file name pointed to by HL
;
MAPNAM:
	CALL	SPACE	;LEADING SPACE
	MOV	A,H
	ORA	L	;NONE?
	JZ	NONAME
	MOV	A,M	;SEE IF ALLOC
	CPI	0E5H	;FREE?
	MVI	A,' '	;MARK ALLOCATED
	JNZ	MPNSP1
	MVI	A,'('	;MARK NOT ALLOCATED (ERASED FILE)
;
MPNSP1:
	CALL	TYPE	;PRINT ALLOCATION INDICATOR (SPACE OR '(')
	PUSH	H	;SAVE POINTER
	MOV	A,M
	CALL	HEX	;SHOW USER NUMBER
	CALL	SPACE
	INX	H	;SKIP USER BYTE
	PUSH	B
	MVI	B,8	;PRINT FILE NAME
	CALL	MAPN2
	MVI	A,'.'	;PRINT DECIMAL SEPARATOR
	CALL	TYPE
	MVI	B,3	;PRINT FILE TYPE
	CALL	MAPN2
	LDA	DUPFLG	;DUPLICATE?
	CALL	TYPE	;SPACE OR STAR
	POP	B
	MOV	A,M	;GET EXT
	CALL	HEX	;PRINT EXTENT NUMBER
	POP	H
	MOV	A,M
	CPI	0E5H	;DELETED ENTRY?
	MVI	A,' '	;PRINT ENDING SPACE
	JNZ	MPNSP2
	MVI	A,')'	;PRINT ALLOCATION FLAG
;
MPNSP2:
	CALL	TYPE	;")" IF ERASED FILE OR SPACE IF NOT
	JMP	FLIP
;
NONAME:
	CALL	ILPRT
	DB	DIM,'    ++ Free ++      ',BRIGHT,0
;
FLIP:
	LDA	TWOUP	;FLIP FLAG FOR TWO ENTRIES PER LINE
	XRI	1
	STA	TWOUP
	JZ	PAGER	;NEW LINE WITH PAGING IF REQUIRED
;
DELIM:
	MVI	A,':'	;PRINT DELIMITER BETWEEN ADJACENT ENTRIES ON LINE
	CALL	TYPE
	JMP	SPACE
;
;Print name pted to by HL, length in B
;
MAPN2:
	MOV	A,M
	ANI	7FH	;STRIP POSSIBLE 2.x ATTRIBUTE BIT
	INX	H
	CPI	' '	;PRINTABLE?
	JC	MAPN2H	;..NO, IN HEX
	CPI	7EH	;7E IS LEADIN ON SOME CRTS
	JC	MAPN2A
;
MAPN2H:
	CALL	BHEX	;PRINT A AS HEX CHARS
	JMP	MAPN2Z
;
MAPN2A:
	CALL	TYPE	;PRINT AS CHAR
;
MAPN2Z:
	DJNZ	MAPN2
	RET
;
;Find which file group (BC) belongs to
;
GETGRP:
	LHLD	DRM	;MAX DIR ENTRY #
	INX	H	;MAKE 1-RELATIVE
	SHLD	FILECT
	LXI	H,0
	SHLD	MFPTR	;SET MULTI-FILE (MORE THAN ONE USER) PTR
	LHLD	DIRECT	;PT TO DIRECTORY
;
GETGLP:
	PUSH	H	;SAVE POINTER TO NAME
	MOV	A,M	;PICK UP DN BYTE
	CPI	0E5H	;ERASED?
	JZ	GETGNF
	LXI	D,14	;NOW GET RECORD COUNT
	DAD	D	;  S2 PORTION ..
	MOV	A,M	;  IS 0 IN CP/M 1.4
	ANI	0FH
	MOV	E,A
	INX	H
	MOV	A,M
	ORA	E
	JZ	GETGNF
	MVI	E,16	;FIRST SET FOR 8-BIT GRPS
	LDA	DSM+1
	ORA	A
	JZ	SMALGP
	MVI	E,8	;NOPE, BIG GROUPS
;
SMALGP:
	MOV	D,A	;SAVE GRP SIZE INDICATOR
;
GETGL2:
	INX	H	;POINTING INTO DM FIELD
	CALL	GRPCMP	;COMPARE BC GP # AGAINST 1 DM FLD
	JNZ	NOTGOT	;JUMP IF NOT FOUND
;
;Found the file
;
	PUSH	H	;SAVE GROUP PTR
	LHLD	MFPTR
	MOV	A,H	;ANY ENTRIES?
	ORA	L
	POP	H	;GET PTR
	XTHL		;SAVE ENTRY START AND SAVE PTR
	JZ	MPFRST	;IF ZERO, THEN FIRST ENTRY
	MVI	A,'*'	;SET MULTI FLAG
	STA	DUPFLG
MPFRST:
	SHLD	MFPTR	;SAVE POINTER
	XTHL		;RESTORE ENTRY START AND GET PTR
NOTGOT:
	DCR	E	;COUNT DOWN
	JNZ	GETGL2	;GO TEST SOME MORE
;
GETGNF:
	POP	H	;NOT THIS ONE
	LXI	D,32	;SO GO TO NEXT
	DAD	D
	XCHG
	LHLD	FILECT	;THERE IS LIMIT TO EVERYTHING
	DCX	H
	SHLD	FILECT
	MOV	A,H
	ORA	L
	XCHG		;RE-ALIGN
	JNZ	GETGLP
;
;Set the allocation address, if any
;
	LHLD	MFPTR	;GET ADDRESS
	RET
;
;COMMAND:  <
;Save the current sector
;	Special Form of <S saves current block onto queue
;	Special Form of <G saves indicated group onto queue
;
SAVE:
	LDA	WRFLG	;READ DONE?
	ORA	A
	JZ	BADW	;NONE TO SAVE
	MOV	A,M	;CHECK FOR 'S'
	CALL	UPCASE	;CAPITALIZE
	CPI	'B'	;BLOCK SAVE
	JZ	QSAV	;SAVE ON STACK
	CPI	'G'	;GROUP SAVE
	JZ	SAVEG
	PUSH	H
	LHLD	SAVBUF	;PT TO SAVBUF
	XCHG		;COPY INTO SAVBUF
	LXI	H,TBUFF	;FROM TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	MVI	A,1	;..SHOW
	STA	SAVEFL	;..SAVED EXISTS
	POP	H	;GET PTR TO NEXT CHAR
	JMP	PROMPT
;
;  Save Block on Queue
;
QSAV:
	INX	H	;SKIP OVER 2ND <
	PUSH	H	;SAVE PTR TO NEXT CHAR
	LHLD	QLST	;SEE IF ANOTHER SAVE WILL FILL QUEUE
	LXI	D,128	;SET HL TO PT TO END OF NEXT SECTOR IN QUEUE
	DAD	D
	XCHG		;DE PTS TO END OF NEXT BLOCK
QSAV0:
	LHLD	QNXT	;SEE IF QUEUE IS FULL NOW
	MOV	A,H	;MAY BE SAME
	CMP	D
	JNZ	QSAV1
	MOV	A,L	;MAY NOT BE SAME
	CMP	E
	JZ	QSAV2	;QUEUE IS FULL, SO ABORT
QSAV1:
	LHLD	QLST	;GET PTR TO LAST QUEUE ELEMENT
	XCHG		;... IN DE
	LXI	H,TBUFF	;COPY FROM TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	CALL	QWRAP	;CHECK FOR WRAP AROUND
	XCHG		;HL PTS TO NEW LAST QUEUE POSITION
	SHLD	QLST	;SAVE HL
	LHLD	QCNT	;INCREMENT SECTOR COUNT
	INX	H
	SHLD	QCNT
	CALL	PRQCNT	;PRINT QUEUE COUNT
	POP	H	;PT TO NEXT CHAR
	JMP	PROMPT
QSAV2:
	CALL	ILPRT
	DB	'Block Queue is Full -- Block Not Saved',CR,LF,0
	LHLD	QCNT	;GET COUNT
	CALL	PRQCNT	;PRINT COUNT
	POP	H	;PT TO NEXT CHAR
	JMP	PRMPTR
;
;  PRINT NUMBER OF ELEMENTS IN QUEUE
;
PRQCNT:
	CALL	DEC	;PRINT AS DECIMAL
	CALL	ILPRT
	DB	DIM,'	Blocks in Queue',BRIGHT,CR,LF,0
	RET
;
;  CHECK TO SEE IF QUEUE ELEMENT PTED TO BY DE SHOULD BE WRAPPED AROUND
;	ON EXIT, DE PTS TO QUEUE ELEMENT WITH WRAP AROUND
;
QWRAP:
	LHLD	BDOS+1	;CHECK FOR WRAP AROUND
	MOV	A,H
	SUI	10	;BELOW CCP
	CMP	D	;WRAP AROUND IF EQUAL
	RNZ
	XCHG
	LHLD	DIRECT	;NEXT ELEMENT IS HERE
	XCHG		;... IN DE
	RET
;
;This routine is common to Save Group (RG) and Write Group (WG); it is used
;  to extract the group number, check it, and position DU3 to it
;  On exit, GROUP = Group Number, GRPDIS = 0, and DU3 is positioned
;
COMG:
	INX	H	;PT TO CHAR AFTER 'G' OF '<G' COMMAND
	PUSH	H	;SAVE PTR TO NEXT CHAR
	MOV	A,M	;GET CHAR AFTER 'G'
	LHLD	GROUP	;GET CURRENT GROUP
	CALL	UPCASE	;CAPITALIZE
	CPI	EOLCH	;CURRENT IF LOGICAL EOL
	JZ	COMG1
	CPI	CR	;CURRENT IF PHYSICAL EOL
	JZ	COMG1
	CALL	HEXIN	;GET GROUP NUMBER IN HEX
	LHLD	DSM	;CHECK FOR BOUNDS ERROR
	CALL	SUBDE	;SUBTRACT GROUP NUMBER FROM DSM
	POP	H	;RESTORE PTR
	JC	OUTLIM	;LIMIT ERROR IF CARRY
	PUSH	H	;SAVE PTR AGAIN
	XCHG		;SAVE GROUP NUMBER
	SHLD	GROUP
COMG1:
	SHLD	TGRP	;TEMPORARY GROUP NUMBER
	XCHG		;GROUP NUMBER IN DE
	XRA	A	;A=0
	STA	GRPDIS	;SET GROUP DISPLACEMENT
	CALL	GTKSEC	;CONVERT GROUP NUMBER TO TRACK AND SECTOR
	CALL	SETTRK	;SET TRACK
	XCHG
	CALL	SETSEC	;SET SECTOR
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;This is the Save Group Routine; it copies the indicated group into the save
;  buffer.
;
SAVEG:
	CALL	COMG	;EXTRACT COMMON GROUP INFO -- GROUP NUMBER AND POS
	PUSH	H
	CALL	ILPRT
	DB	'Reading from Group ',0
	LHLD	GROUP	;GET CURRENT GROUP
	MOV	B,H	;VALUE IN BC
	MOV	C,L
	CALL	HEXB	;PRINT AS HEX
	CALL	ILPRT
	DB	CR,LF,0
	LHLD	QLST	;LAST PTR USED FOR READ
	SHLD	QPTR
	POP	H
	MVI	A,0	;SET COPY FUNCTION TO SAVE
	STA	CPYFCT	;0=READ, 0FFH=WRITE
;
;Group Copy Routine -- if CPYFCT = 0, Read Group; if CPYFCT = 0FFH, Write Group
;
COPYG:
	PUSH	H	;SAVE PTR TO NEXT CHAR IN COMMAND LINE
	CALL	NORITE	;POSITIONING LOST
	XCHG		;SAVE HL
	LHLD	QPTR	;PT TO NEXT QUEUE POSITION
	XCHG		;... IN DE
	LDA	BLM	;GET NUMBER OF BLOCKS/GROUP
	INR	A	; ADD 1 TO BLM FOR CORRECT COUNT
	MOV	B,A	;COUNT IN B
;
COPYGL:
	PUSH	B	;SAVE COUNT
	PUSH	D	;SAVE PTR TO NEXT BLOCK TO LOAD
	MOV	B,D	;SET BC=DE FOR SET DMA
	MOV	C,E
	CALL	SETDMA	;SET ADDRESS TO LOAD
	LDA	CPYFCT	;READ OR WRITE?
	ORA	A	;0=READ
	JNZ	COPYGLW
	CALL	READ	;READ BLOCK
	LHLD	QCNT	;INCREMENT QUEUE ELEMENT COUNT
	INX	H
	SHLD	QCNT
	JMP	COPYGL0
COPYGLW:
	LHLD	QCNT	;QUEUE EMPTY?
	MOV	A,H
	ORA	L
	JZ	QEMPTY
	CALL	PWRITE	;WRITE BLOCK (NO CHECK)
	LHLD	QCNT	;DECREMENT QUEUE ELEMENT COUNT
	DCX	H
	SHLD	QCNT
COPYGL0:
	CALL	NXTSEC	;COMPUTE NEXT SECTOR ADDRESS
	LHLD	CURTRK	;GET NEXT TRACK ADDRESS
	XCHG		;... IN DE
	CALL	SETTRK	;SET IT
	LHLD	CURSEC	;GET NEXT SECTOR ADDRESS
	XCHG		;... IN DE
	CALL	SETSEC	;SET IT
	POP	D	;GET PTR TO NEXT BLOCK
	POP	B	;GET COUNTER
	LXI	H,80H	;OFFSET TO NEXT BLOCK
	DAD	D
	SHLD	QPTR
	XCHG		;DE PTS TO NEXT BLOCK
	CALL	QWRAP	;ALLOW WRAP AROUND IN QUEUE
	LDA	CPYFCT	;0=READ
	ORA	A	;NO QUEUE OVERFLOW CHECK IF WRITE
	JNZ	COPYGL1
	LHLD	QNXT	;CHECK FOR QUEUE OVERFLOW
	MOV	A,H	;MUST NOT BE EQUAL
	CMP	D
	JNZ	COPYGL1
	MOV	A,L
	CMP	E
	JZ	QSAV2
COPYGL1:
	DJNZ	COPYGL	;LOOP UNTIL FINISHED
	LHLD	QCNT	;PRINT COUNT
	CALL	PRQCNT
	LHLD	QPTR	;GET QUEUE PTR
	LDA	CPYFCT	;RESET PROPER QUEUE PTR
	ORA	A	;0=READ
	JZ	COPYGL2
	SHLD	QNXT	;NEXT PTR USED FOR WRITE
	JMP	COPYGL3
COPYGL2:
	SHLD	QLST	;LAST PTR USED FOR READ
COPYGL3:
	LXI	B,TBUFF	;RESET DMA ADDRESS
	CALL	SETDMA
	XRA	A	;A=0
	STA	WRFLG	;SET NO READ DONE
	LHLD	TGRP	;GET GROUP NUMBER
	XCHG		;... IN DE
	POP	H	;GET PTR TO NEXT CHAR
	JMP	POSGRP	;POSITION TO GROUP IN DE AND CONTINUE PROCESSING
;
;COMMAND:  >
;Restore the current sector
;	Special Form >S gets next block from queue
;	Special Form >G gets next group from queue
;
RESTOR:
	MOV	A,M	;CHECK FOR SPECIAL FORM
	CALL	UPCASE	;CAPITALIZE
	CPI	'B'	;BLOCK SAVE?
	JZ	QRESTOR
	CPI	'G'	;GROUP SAVE?
	JZ	RESTRG
	LDA	SAVEFL	;SAVE DONE PREVIOUSLY?
	ORA	A
	JZ	NOSAVE	;NONE TO SAVE
	PUSH	H
	LHLD	SAVBUF	;COPY FROM SAVBUF
	LXI	D,TBUFF	;INTO TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	POP	H	;GET PTR TO NEXT CHAR
	JMP	PROMPT
;
;  Restore Sector from Queue
;
QRESTOR:
	INX	H	;PT TO NEXT CHAR
	PUSH	H	;SAVE PTR ON STACK
	LHLD	QCNT	;GET ELEMENT COUNT
	MOV	A,H	;EMPTY?
	ORA	L
	JZ	QEMPTY	;ABORT IF EMPTY
	DCX	H	;COUNT DOWN
	SHLD	QCNT
	CALL	PRQCNT	;PRINT COUNT
	LHLD	QNXT	;PT TO NEXT ELEMENT IN QUEUE
	LXI	D,TBUFF	;COPY INTO TBUFF
	MVI	B,128	;128 BYTES
	CALL	MOVE
	XCHG		;DE=PTR TO NEXT ELEMENT IN QUEUE
	CALL	QWRAP	;CHECK FOR WRAP AROUND
	XCHG		;HL PTS TO NEXT ELEMENT IN QUEUE
	SHLD	QNXT	;SAVE PTR
	POP	H	;RESTORE PTR
	JMP	PROMPT
QEMPTY:
	CALL	ILPRT
	DB	'Error -- Queue Empty',CR,LF,0
	POP	H	;RESTORE NEXT CHAR PTR
	JMP	PRMPTR
;
;Write Group Loaded in GBUFF to Disk
;
RESTRG:
	CALL	COMG	;GET GROUP NUMBER FROM COMMAND LINE AND POS
	PUSH	H
	CALL	ILPRT
	DB	'Writing to Group ',0
	LHLD	GROUP	;GET GROUP NUMBER
	MOV	B,H	;VALUE IN BC
	MOV	C,L
	CALL	HEXB	;PRINT IN HEX
	CALL	ILPRT
	DB	CR,LF,0
	LHLD	QNXT	;NEXT PTR USED FOR WRITE
	SHLD	QPTR
	POP	H
	MVI	A,0FFH	;WRITE FUNCTION
	STA	CPYFCT	;COPY FUNCTION FOR GROUP COPY ROUTINE
	JMP	COPYG	;GROUP COPY ROUTINE
;
NOSAVE:
	CALL	ILPRT
	DB	'++ No "<" Save Command Issued ++'
	DB	CR,LF,0
	JMP	PRMPTR
;
;Move (HL) to (DE) length in B
;
MOVE:
	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DJNZ	MOVE
	RET
;
NORITE:
	XRA	A	;GET 0
	STA	WRFLG	;CAN'T WRITE NOW
	RET
;
;No match in search, try next char
;
SRNOMT:
	POP	H
	CALL	CTLCS	;ABORT?
	JNZ	SEARCH	;..YES
	LHLD	INBUF
	MVI	M,CR
	JMP	CLCGRP	;SHOW WHERE STOPPED
;
;COMMAND:  =
;Search for character string
;
SEARCH:
	PUSH	H	;SAVE STRING POINTER
;
SRCHL:
	CALL	RDBYTE	;GET A BYTE
	MOV	B,A	;SAVE IT
	MOV	A,M	;CHECK NEXT MATCH CHAR.
	CPI	'<'	;WILL IT BE HEX?
	MOV	A,B	;RESTORE DISK CHAR
	JZ	SRCHL1
	ANI	7FH	;NEXT CHAR IS ASCII...STRIP BIT 7
;
SRCHL1:
	PUSH	PSW
	CALL	GETVAL	;GET SEARCH VALUE
	MOV	B,A
	POP	PSW
	CMP	B	;MATCH?
	JNZ	SRNOMT	;NO MATCH
	INX	H
	MOV	A,M	;DONE?
	CPI	CR	;END OF LINE?
	JZ	SREQU
	CPI	EOLCH	;LOGICAL EOL?
	JNZ	SRCHL
;
;Got match
;
SREQU:
	CALL	ILPRT
	DB	'= at ',0
	LDA	BUFAD
	ANI	7FH
	CALL	HEX
	CALL	CRLF
	JMP	CLCGRP
;
;Get value from input buffer
;
GETVAL:
	MOV	A,M	;GET NEXT CHAR
	CPI	'<'	;HEX ESCAPE?
	RNZ		;NO, RETURN
;"<<" means one "<"
	INX	H
	MOV	A,M
	CPI	'<'
	RZ
;Got hex
	PUSH	D
	CALL	HEXIN	;GET VALUE
	CPI	'>'	;PROPER DELIM?
	MOV	A,E	;GET VALUE
	POP	D
	JNZ	WHAT	;ERROR
	RET
;
;Read a byte at a time from disk
;
RDBYTE:
	PUSH	H
	LDA	FTSW	;FIRST READ?
	ORA	A
	JNZ	READ1
	LHLD	BUFAD
	MOV	A,L
	ORA	A	;IN BUFFER?
	JM	NORD	;YES, SKIP READ
;
;Have to read
;
	CALL	NXTSEC	;ADVANCE TO NEXT BLOCK
;
READ1:
	XRA	A
	STA	FTSW	;NOT FIRST READ
	LHLD	CURSEC
	XCHG
	CALL	SETSEC
	LHLD	CURTRK
	XCHG
	CALL	SETTRK
	CALL	READ
	CALL	CLCSUB
	LXI	H,TBUFF
;
NORD:
	MOV	A,M
	INX	H
	SHLD	BUFAD
	POP	H
	RET
;
;COMMAND:  V
;View the file in ASCII starting at
;current sector, stepping thru the disk
;
VIEW:
	LDA	WRFLG
	ORA	A
	JZ	BADDMP
	CALL	DECIN	;GET DISPL IF ANY
	PUSH	H
	MOV	A,E
	ORA	A
	JNZ	VIEWLP
	INR	E	;DFLT=1
;
VIEWLP:
	LXI	H,TBUFF	;TO DATA
;
VEWCHR:
	CALL	CTLCS	;ABORT?
	JZ	VEWEND
	MOV	A,M	;GET NEXT CHAR
	CPI	1AH	;EOF?
	JZ	VEWEOF
	ANI	7FH	;MASK
	CPI	7EH	;ESC CHAR FOR H1500
	JNC	VIEWHX	;SHOW RUBOUT AND TILDE AS HEX
	CPI	' '
	JNC	VIEWPR
	CPI	CR	;CR PASS
	JZ	VIEWPR
	CPI	LF	;LF PASS
	JZ	VIEWPR
	CPI	TAB	;TAB PASS
	JZ	VIEWPR
;
VIEWHX:
	MOV	A,M	;NOT ASCII...PRINT AS <NN>
	CALL	BHEX
	JMP	VIEWNP
;
VIEWPR:
	CALL	TYPE
;
VIEWNP:
	INR	L
	JNZ	VEWCHR
	DCR	E
	JZ	VEWEND
	PUSH	D	;SAVE COUNT
	CALL	NXTSEC
	LHLD	CURSEC
	XCHG
	CALL	SETSEC	
	LHLD	CURTRK
	XCHG
	CALL	SETTRK
	CALL	READ
	POP	D	;RESTORE COUNT
	JMP	VIEWLP
;
VEWEOF:
	CALL	ILPRT
	DB	CR,LF,DIM,' ++ EOF ++',BRIGHT,CR,LF,0
;
VEWEND:
	POP	H
	CALL	CRLF
	JMP	CLCGRP
;
;COMMAND:  A or D
;Dump in hex or ASCII
;
DUMP:
	LDA	WRFLG
	ORA	A
	JNZ	DUMPOK
;
BADDMP:
	CALL	ILPRT
	DB	'++ Can''t dump, no sector read ++',CR,LF,0
;
EXPL:
	CALL	ILPRT
	DB	'Use G command following F,',CR,LF
	DB	'or R or S following T',CR,LF,0
	JMP	PRMPTR
;
DUMPOK:
	MOV	A,M	;GET NEXT CHAR
	CPI	EOLCH	;LOGICAL EOL?
	JZ	DUMPDF	;DFLT
	CPI	CR	;PHYSICAL EOL?
	JNZ	DMPNDF
;
;Use default
;
DUMPDF:
	LXI	B,TBUFF
	LXI	D,0FFH
	JMP	DUMP1
;
DMPNDF:
	CALL	DISP
	MOV	B,D
	MOV	C,E
	CPI	CR
	JZ	DUMP1
	CPI	EOLCH
	JZ	DUMP1
	INX	H	;SKIP SEPCH
	CALL	DISP
;
;BC = start, DE = end
;
DUMP1:
	PUSH	H	;SAVE COMMAND POINTER
	MOV	H,B
	MOV	L,C
;
DUMPLP:
	CALL	DUMPHL	;PERFORM DUMP OF DIR ENTRY AT HL
	POP	H	;RESTORE HL
	JMP	PROMPT
;
; PERFORM DUMP AT HL
;
DUMPHL:
	CALL	STNDOUT	;DIM
	MOV	A,L
	ANI	7FH
	CALL	HEX	;PRINT HEX VALUE
	CALL	STNDEND	;BRIGHT
	CALL	SPACE
	CALL	SPACE
	LDA	DUMTYP
	CPI	'A'
	JZ	DUMPAS
	PUSH	H	;SAVE START
;
; DUMP 16 BYTES STARTING AT HL (CHECK FOR DE TERMINATION)
;
DHEX:
	MOV	A,M
	CALL	HEX	;PRINT HEX VALUE PTED TO BY HL
	MOV	A,L
	ANI	3
	CPI	3	;EXTRA SPACE EVERY 4
	CZ	SPACE
	MOV	A,L
	ANI	7
	CPI	7	;TWO EXTRA SPACES EVERY 8
	CZ	SPACE
	MOV	A,E	;CHECK FOR END OF BYTES TO DUMP
	CMP	L
	JZ	DPOP
	INX	H
	MOV	A,L	;CHECK FOR END OF 16 BYTES
	ANI	0FH
	JNZ	DHEX
;
DPOP:
	CALL	CTLCS	;ABORT?
	JZ	PRMPTR
	LDA	DUMTYP	;CHECK FOR ASCII ALSO
	CPI	'H'
	JZ	DNOAS	;HEX ONLY
	POP	H	;GET START ADDR
;
;  DUMP ASCII CHARS - HL PTS TO FIRST BYTE
;
DUMPAS:
	CALL	ASTER	;PRINT FIRST ASTERISK TO SEPARATE TEXT
;
DCHR:
	MOV	A,M	;GET CHAR
	ANI	7FH
	CPI	' '
	JC	DPER
	CPI	7FH	;TRAP DEL
	JC	DOK
;
DPER:
	MVI	A,'.'	;PRINT PRINTING CHAR
;
DOK:
	CALL	TYPE	;PRINT CHAR
	MOV	A,E	;CHECK FOR END OF DUMP
	CMP	L
	JZ	DEND
	INX	H
	MOV	A,L	;CHECK FOR END OF 16 BYTES
	ANI	0FH
	JNZ	DCHR
;
; END OF ASCII DUMP
;
DEND:
	CALL	ASTER	;PRINT ENDING ASTERISK
	CALL	CRLF	;NEW LINE
	PUSH	D
	CALL	CTLCS	;ABORT?
	POP	D
	JZ	PRMPTR
	MOV	A,E	;DONE WITH DUMP?
	CMP	L
	JNZ	DUMPHL
	RET
;
; NO ASCII DUMP
;
DNOAS:
	POP	B	;CLEAR STACK (START ADDRESS OF DUMP)
	CALL	CRLF	;NEW LINE
	MOV	A,E	;DONE WITH DUMP?
	CMP	L
	JNZ	DUMPHL
	RET
;
;COMMAND:  G
;Position
;
POS:
	PUSH	PSW
	MOV	A,M
	CPI	EOLCH	;LOGICAL EOL?
	JZ	POSINQ
	CPI	CR	;PHYSICAL EOL?
	JNZ	POSOK
;
POSINQ:
	POP	PSW
	JMP	INQ
;
POSOK:
	POP	PSW
	CPI	'T'	;TRACK?
	JZ	POSTKD
	CPI	'S'	;SECTOR?
	JZ	POSSCD
	CPI	'G'	;GROUP?
	JZ	POSGPH
	JMP	WHAT	;ERROR OTHERWISE
;
;Position to Track
;
POSTKD:
	CALL	DECIN	;GET NUMBER IN DECIMAL
;
POSTRK:
	PUSH	H
	LHLD	MAXTRK	;CHECK FOR BEYOND END OF DISK
	CALL	SUBDE
	POP	H
	JC	OUTLIM
	CALL	SETTRK	;SET TRACK
	CALL	NORITE	;TRACK DOESN'T READ
	MVI	A,1
	STA	NOTPOS	;SHOW NOT POSITIONED
	JMP	CLCGRP
;
;Position to Sector
;
POSSCD:
	CALL	DECIN	;GET NUMBER IN DECIMAL
	MOV	A,D
	ORA	E
	JZ	WHAT	;DON'T ALLOW SECTOR 0
;
POSSEC:
	PUSH	H
	LHLD	SPT	;CHECK FOR WITHIN RANGE
	CALL	SUBDE
	POP	H
	JC	WHAT
	CALL	SETSEC	;SET SECTOR
	CALL	READ	;READ
	XRA	A
	STA	NOTPOS	;POSITIONED OK
;
;Calculate Group Number/Group Displacement and Print
;
CLCGRP:
	CALL	CLCSUB
	JMP	INQ
;
;Calculate group from track and sector
;  On exit, GROUP = Group Number and GRPDIS = Displacement within Group
;
CLCSUB:
	PUSH	H
	LHLD	SYSTRK
	XCHG
	LHLD	CURTRK
	CALL	SUBDE	;COMPUTE RELATIVE TRACK NUMBER (SKIP SYSTEM TRACKS)
	XCHG
	LHLD	SPT	;MULTIPLY BY NUMBER OF SECTORS/TRACK
	CALL	MULT
	XCHG		;DE=TOTAL NUMBER OF SECTORS IN TRACKS
	LHLD	CURSEC	;GET SECTOR OFFSET FROM BEGINNING OF TRACK
	DCX	H
	DAD	D	;HL=TOTAL NUMBER OF SECTORS WITH OFFSET
	LDA	BLM
	MOV	B,A
	MOV	A,L
	ANA	B
	STA	GRPDIS	;DISPLACEMENT WITHIN GROUP
	LDA	BSH
	MOV	B,A
;
CLCLOP:
	CALL	ROTRHL
	DJNZ	CLCLOP
	SHLD	GROUP	;GROUP NUMBER
	POP	H
	RET
;
;Position in the directory after a find
;(Does not work in CP/M-2.x)
;
POSDIR:
	PUSH	H	;SAVE INBUF
	LHLD	BSH
	XRA	A
	STA	FINDFL	;CANCEL POS REQ
	LDA	DIRPOS	;GET POSITION
	RAR
	RAR
	PUSH	PSW
	ANA	H
	STA	GRPDIS
	POP	PSW
;
POSDLP:
	RAR
	DCR	L
	JNZ	POSDLP
	ANI	1	;GET GROUP
	MOV	L,A	;SETUP FOR POSGP2
	MVI	H,0
	SHLD	GROUP
	XCHG
;
POSGP2:
	CALL	GTKSEC	;CONVERT GROUP TO SECTOR/TRACK
	CALL	SETTRK	;SET TRACK
	XCHG
	CALL	SETSEC	;SET SECTOR
	CALL	READ	;READ BLOCK
	XRA	A
	STA	NOTPOS	;NOW POSITIONED
	POP	H
	JMP	INQ
;
;Position to Group
;
POSGPH:
	CALL	HEXIN	;GET PARAMETER
;
;Position to Group Numbered in DE and Print Position
;
POSGRP:
	CALL	DEGROUP	;GOTO GROUP
	JC	OUTLIM
	JMP	INQ	;PRINT POSITION
;
;Position to Group Numbered in DE
; Return with Carry Set if Out of Limits
;
DEGROUP:
	PUSH	H
	LHLD	DSM	;CHECK FOR WITHIN BOUNDS
	CALL	SUBDE
	POP	H
	RC
	PUSH	H	;SAVE HL
	XCHG
	SHLD	GROUP	;SET GROUP NUMBER
	XCHG
	XRA	A
	STA	GRPDIS	;SET ZERO DISPLACEMENT
	CALL	GTKSEC	;CONVERT GROUP TO SECTOR/TRACK
	CALL	SETTRK	;SET TRACK
	XCHG
	CALL	SETSEC	;SET SECTOR
	CALL	READ	;READ BLOCK
	XRA	A	;SET NC AND FLAG
	STA	NOTPOS	;NOW POSITIONED
	POP	H
	RET
;
;Convert Group Number in DE to Sector and Track; also, GRPDIS = Offset in Grp
;  On exit, DE = Track Number, HL = Sector Number
;
GTKSEC:
	MOV	H,D	;HL=GROUP NUMBER
	MOV	L,E
	LDA	BSH	;GET NUMBER OF SECTORS IN GROUP
;
GLOOP:
	DAD	H
	DCR	A
	JNZ	GLOOP
	LDA	GRPDIS	;ADD IN DISPLACEMENT WITHIN GROUP
	ADD	L	;CAN'T CARRY
	MOV	L,A
;
;Divide by number of sectors, quotient=track, remainder=sector
;
	XCHG		;DE=TOTAL NUMBER OF SECTORS
	LHLD	SPT	;GET NUMBER OF SECTORS/TRACK
	CALL	NEG	;HL = -SECTORS/TRACK
	XCHG
	LXI	B,0	;SET TRACK COUNTER TO ZERO
;
DIVLP:
	INX	B	;INCREMENT TRACK COUNT
	DAD	D	;SUBTRACT SECTORS/TRACK FROM SECTORS TOTAL
	JC	DIVLP
	DCX	B	;ADJUST TRACK COUNT
	XCHG
	LHLD	SPT	;ADD SECTORS/TRACK BACK IN TO ADJUST
	DAD	D	;HL=NUMBER OF SECTORS ON LAST TRACK OF GROUP
	PUSH	H
	LHLD	SYSTRK	;ADD IN NUMBER OF SYSTEM TRACKS
	DAD	B
	XCHG		;DE=TRACK NUMBER
	POP	H
	INX	H	;HL=SECTOR NUMBER
	RET
;
;COMMAND:  F
;Find Directory Entry for specified file
;
POSFIL:
	CALL	NORITE
	MVI	A,1
	STA	FINDFL	;SO WE POSITION LATER
	LXI	D,FCB
	XRA	A	;LOGGED IN DISK
	STAX	D
	INX	D
	MVI	B,8
	CALL	MVNAME
	MVI	B,3
	CALL	MVNAME
	MVI	A,'?'
	STAX	D	;LOOK IN ALL EXTENTS
	MVI	A,'D'	;SET TYPE OF DUMP TO FULL
	STA	DUMTYP
	PUSH	H	;SAVE PTR TO NEXT CHAR
	LXI	D,FCB
	MVI	C,SRCHF
	CALL	BDOS
	INR	A
	JNZ	FLOK
	STA	DIRPOS	;GRP 0 IF NOT FOUND
	CALL	ILPRT
	DB	'++ File Not Found ++',CR,LF,0
	POP	H	;RESTORE PTR TO NEXT CHAR
	JMP	PROMPT
;
FLOK:
	DCR	A
	STA	DIRPOS	;SAVE POS. IN DIR
	ANI	3
	MOV	L,A
	MVI	H,0
	DAD	H	;X32 BYTES/ENTRY
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	LXI	D,TBUFF
	DAD	D	;HL POINTS TO ENTRY
	LXI	D,32
	XCHG
	DAD	D
	XCHG
	CALL	DUMPHL	;PRINT DIR ENTRY
	LXI	D,FCB	;LOOK FOR NEXT EXTENT
	MVI	C,SRCHN
	CALL	BDOS
	INR	A
	JNZ	FLOK
	POP	H	;RESTORE PTR TO NEXT CHAR
	JMP	PROMPT
;
MVNAME:
	MOV	A,M	;GET NEXT CHAR OF FILE NAME/TYPE
	CPI	'.'	;END OF FILE NAME?
	JZ	MVIPAD	;PAD OUT IF SO
	CPI	CR	;END OF ENTRY?
	JZ	PAD	;PAD OUT IF SO
	CPI	EOLCH	;END OF ENTRY?
	JZ	PAD	;PAD OUT IF SO
	CALL	UPCASE	;CAPITALIZE
	STAX	D	;STORE
	INX	H	;PT TO NEXT
	INX	D
	DJNZ	MVNAME
	MOV	A,M	;CHECK FOR ERROR
	CPI	CR	;OK IF EOL
	RZ
	CPI	EOLCH	;OK IF LOGICAL EOL
	RZ
	INX	H
	CPI	'.'	;OK IF DECIMAL
	RZ
	JMP	WHAT
;
MVIPAD:
	INX	H
;
PAD:
	MVI	A,' '	;PRINT PADDING SPACES
	STAX	D
	INX	D
	DJNZ	PAD
	RET
;
;COMMAND:  +
;Advance to Next Logical Sector
;
PLUS:
	LXI	D,1	;DFLT TO 1 SECT
	MOV	A,M	;GET NEXT CHAR
	CPI	CR	;CR?
	JZ	PLUSGO	;..YES, DFLT TO 1
	CPI	EOLCH
	JZ	PLUSGO
	CALL	DECIN	;GET #
	MOV	A,D
	ORA	E
	JNZ	PLUSGO
	LXI	D,1	;SET 1 IF VALUE OF ZERO
;
PLUSGO:
	CALL	NXTSEC	;ADVANCE TO NEXT LOGICAL SECTOR
	DCX	D	;MORE TO GO?
	MOV	A,D
	ORA	E
	JNZ	PLUSGO	;..YES
;
;Ok, incremented to sector.  Setup and read
;
PLUSMI:
	PUSH	H
	LHLD	CURSEC
	XCHG
	CALL	SETSEC	;SET SECTOR
	LHLD	CURTRK
	XCHG
	CALL	SETTRK	;SET TRACK
	POP	H
	CALL	READ	;READ IT
	JMP	CLCGRP	;CALCULATE GROUP AND DISPLAY
;
;COMMAND:  -
;Back up to previous sector
;
MINUS:
	LXI	D,1	;SET DFLT
	MOV	A,M	;GET CHAR
	CPI	CR	;CR?
	JZ	MINGO	;..YES, DFLT=1
	CPI	EOLCH
	JZ	MINGO
	CALL	DECIN	;..NO, GET ##
	MOV	A,D
	ORA	E
	JNZ	MINGO
	LXI	D,1	;ASSUME 1
;
MINGO:
	CALL	LSTSEC	;BACK UP ONE SECTOR
	DCX	D	;COUNT DOWN ON NUMBER OF TIMES TO BACKUP
	MOV	A,D
	ORA	E
	JNZ	MINGO
	JMP	PLUSMI	;READ BLOCK
;
;Go to last sector
; Wrap around to last sector of previous track or last sector of
; last track, as necessary
;
LSTSEC:
	PUSH	H
	LHLD	CURSEC	;BACK UP SECTOR
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	LSEC1
	LHLD	CURTRK	;BEYOND SECTOR ZERO, SO BACK UP TRACK
	MOV	A,H
	ORA	L
	JNZ	LSEC0
	LHLD	MAXTRK	;WRAP TO END OF DISK
	SHLD	CURTRK
	LHLD	MAXSEC
	JMP	LSEC1
;
LSEC0:
	DCX	H
	SHLD	CURTRK
	LHLD	SPT	;GET NUMBER OF SECTORS/TRACK
;
LSEC1:
	SHLD	CURSEC	;SET NEW CURRENT SECTOR
	POP	H
	RET
;
;Go to next sector
;  On exit, CURSEC = Current Sector and CURTRK = Current Track
;
NXTSEC:
	PUSH	H
	PUSH	D
	LHLD	CURSEC	;INCREMENT CURRENT SECTOR
	INX	H
	XCHG
	LHLD	SPT	;CHECK TO SEE IF BEYOND END OF TRACK
	CALL	SUBDE
	XCHG
	JNC	NEXTOK
	LHLD	CURTRK	;BEYOND END OF TRACK, SO INCR CURRENT TRACK
	INX	H
	XCHG
	LHLD	MAXTRK	;SEE IF BEYOND END OF DISK
	CALL	SUBDE
	JNC	TRASK
	LXI	D,0	;WRAP TO START OF DISK
;
TRASK:
	XCHG
	SHLD	CURTRK	;SET NEW CURRENT TRACK
	LXI	H,1	;SET SECTOR 1
;
NEXTOK:
	SHLD	CURSEC	;SET NEW CURRENT SECTOR
	POP	D
	POP	H
	RET
;
;Tell what group, displacement, track, sector, physical sector
;
INQ:
	CALL	INQSUB
	JMP	PROMPT
;
;Position inquiry subroutine
;Executed via: G S or T (with no operands)
;
INQSUB:
	PUSH	H
	LHLD	SYSTRK	;CHECK IF IN SYSTEM TRACKS
	XCHG
	LHLD	CURTRK
	CALL	SUBDE
	JC	NOGRP
	CALL	ILPRT	;PRINT GROUP NUMBER IF NOT IN SYSTEM TRACKS
	DB	DIM,'Group = ',BRIGHT,0
	LHLD	GROUP
	MOV	B,H
	MOV	C,L
	CALL	HEXB	;PRINT GROUP NUMBER IN BC
	MVI	A,':'
	CALL	TYPE
	LDA	GRPDIS
	CALL	HEX	;PRINT GROUP DISPLACEMENT IN A
	MVI	A,','
	CALL	TYPE
;
NOGRP:
	CALL	ILPRT	;PRINT TRACK NUMBER
	DB	DIM,' Track = ',BRIGHT,0
	LHLD	CURTRK
	CALL	DEC	;TRACK NUMBER IN DECIMAL
	CALL	ILPRT	;PRINT SECTOR NUMBER
	DB	DIM,', Sector = ',BRIGHT,0
	LHLD	CURSEC
	CALL	DEC	;SECTOR NUMBER IN DECIMAL
	CALL	ILPRT	;PRINT PHYSCIAL SECTOR NUMBER
	DB	DIM,', Physical Sector = ',BRIGHT,0
	LHLD	PHYSEC
	CALL	DEC	;PHYSICAL SECTOR NUMBER IN DECIMAL
	CALL	CRLF
	POP	H
	RET
;
;COMMAND:  C
;Change Contents of Current Block
;
CHG:
	MOV	A,M	;GET TYPE (HEX, ASCII)
	CALL	UPCASE
	PUSH	PSW	;SAVE "H" OR "A"
	INX	H
	CALL	HEXIN	;GET DISP IN HEX
	CALL	DISP1	;VALIDATE DISP TO DE
	INX	H
	LXI	B,0	;SHOW NO 'THRU' ADDR
	CPI	'-'	;TEST DELIM FR. DISP
	JNZ	CHGNTH	;NO THRU
	PUSH	D	;SAVE FROM
	CALL	HEXIN
	CALL	DISP1	;GET THRU
	INX	H	;SKIP END DELIM
	MOV	B,D
	MOV	C,E	;BC = THRU
	POP	D	;GET FROM
	JMP	CHGAH
;
CHGNTH:
	CPI	SEPCH
	JNZ	WHAT
;
CHGAH:
	POP	PSW
	CPI	'H'	;HEX?
	JZ	CHGHEX
	CPI	'A'	;ASCII?
	JNZ	WHAT
;
;Change ASCII
;
CHGALP:
	MOV	A,M	;GET CHAR
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
;
;The following print of the deleted byte is commented out; if leading
;  semicolons are removed, deleted bytes will be printed
;
;	LDAX	D	;GET BYTE THAT IS REPLACED
;	CPI	' '
;	JC	CHGAHX
;	CPI	7EH	;DON'T PRINT ESC CHAR FOR H1500
;	JNC	CHGAHX
;	JMP	CHGA2
;
;CHGAHX:
;	CALL	BHEX
;	JMP	CHGA3
;
;CHGA2:
;	CALL	TYPE
;
;End of print of delete bytes
;
CHGA3:
	SHLD	BACK	;IN CASE "THRU"
	CALL	GETVAL	;GET ASCII OR <HEX> VALUE
	STAX	D	;UPDATE BYTE
	INX	H	;PT TO NEXT INPUT CHAR
;
;See if 'THRU' requested
;
	MOV	A,C
	ORA	A
	JZ	CHANTH
	CMP	E	;DONE?..
	JZ	PROMPT	;..YES
	LHLD	BACK
;
CHANTH:
	INR	E
	JNZ	CHGALP
	MOV	A,M
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
	JMP	WHAT
;
;Change hex
;
CHGHCM:
	INX	H
;
CHGHEX:
	MOV	A,M	;GET HEX DIGIT
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
	CPI	SEPCH	;DELIM?
	JZ	CHGHCM
	PUSH	D
	SHLD	HEXAD	;IN CASE 'THRU'
	CALL	HEXIN	;POSITIONS TO DELIM
	MOV	A,E	;GET VALUE
	POP	D	;..ADDR
;
;The following comments out the echo of the deleted byte; removing the
;  leading semicolons restores the echo
;
;	PUSH	PSW	;SAVE VALUE
;	LDAX	D	;GET OLD
;	CALL	HEX	;ECHO IN HEX
;	POP	PSW	;GET NEW
;
;End of echo of bytes
;
	STAX	D	;SAVE NEW BYTE
	MOV	A,C	;SEE IF 'THRU'
	ORA	A
	JZ	CHHNTH	;..NO.
	CMP	E	;..YES, DONE?
	JZ	PROMPT
	LHLD	HEXAD	;..NO: MORE
;
CHHNTH:
	INR	E
	JNZ	CHGHEX
	MOV	A,M
	CPI	CR
	JZ	PROMPT
	CPI	EOLCH
	JZ	PROMPT
	JMP	WHAT
;
;COMMAND:  R
;Read Current Block into TBUFF
;COMMAND:  RG
;Read Specified Group into GBUFF
;
DOREAD:
	LDA	NOTPOS	;POSITIONED?
	ORA	A
	JNZ	CANTRD
	CALL	READ	;READ BLOCK
	JMP	PROMPT
;
CANTRD:
	CALL	ILPRT
	DB	'++ Can''t read - not positioned ++',CR,LF
	DB	'Position by:',CR,LF
	DB	'	Track then Sector, or',CR,LF
	DB	'	Group',CR,LF,0
	JMP	PROMPT
;
;COMMAND:  W
;Write Current Block to Disk
;COMMAND:  WG
;Write Specified Group from GBUFF
;
DORITE:
	CALL	WRITE	;DO WRITE
	JMP	PROMPT
;
;Print Byte in A as Hex Digits
;
BHEX:
	PUSH	PSW
	MVI	A,'<'
	CALL	TYPE
	POP	PSW
	CALL	HEX
	MVI	A,'>'
	CALL	TYPE
	RET
;
;Print Number in BC as Hex Digits
;  HEXB does not print MS Byte if DSM shows small disk size
;  HEXB1 prints BC regardless
;
HEXB:
	LDA	DSM+1
	ORA	A
	JZ	HEXX
HEXB1:
	MOV	A,B
	CALL	HEX
;
HEXX:
	MOV	A,C
;
;Print Byte in A as 2 Hex Digits
;
HEX:
	PUSH	PSW
	RAR		;GET HIGH NYBBLE
	RAR
	RAR
	RAR
	CALL	NIBBL	;PRINT IT
	POP	PSW	;GET LOW NYBBLE
;
NIBBL:
	ANI	0FH	;MASK LOW NYBBLE
	CPI	10	;0-9?
	JC	HEXNU
	ADI	7	;CONVERT TO A-F
;
HEXNU:
	ADI	'0'	;CONVERT TO ASCII
	JMP	TYPE	;PRINT IT
;
;Decimal output routine
;  Print Number in HL as decimal digits
;
DEC:
	PUSH	B
	PUSH	D
	PUSH	H
	XRA	A	;SET NO LEADING DIGIT
	STA	DDIG
	LXI	B,10000
	CALL	DPRT
	DAD	B
	LXI	B,1000
	CALL	DPRT
	DAD	B
	LXI	B,100
	CALL	DPRT
	DAD	B
	LXI	B,10
	CALL	DPRT
	DAD	B
	MOV	A,L	;ALWAYS PRINT LSD
	ADI	'0'	;ASCII
	CALL	TYPE
	POP	H
	POP	D
	POP	B
	RET
DPRT:
	PUSH	B	;SAVE BC
	MVI	D,0FFH	;SET -1
DPRTL:
	INR	D	;ADD 1 TO OUTPUT DIGIT
	MOV	A,L	;L-C
	SUB	C
	MOV	L,A
	MOV	A,H	;H-B
	SBB	B
	MOV	H,A
	JNC	DPRTL
	POP	B	;RESTORE BC
	LDA	DDIG	;GET LEADING DIGIT FLAG
	ORA	D	;CHECK FOR ZERO STILL
	STA	DDIG	;SET FLAG
	MOV	A,D	;GET DIGIT TO PRINT
	RZ		;ABORT IF BOTH ZERO
	ADI	'0'	;ASCII
	JMP	TYPE
DDIG:	DS	1	;TEMP FOR DEC USE ONLY
;
;Print <SP>
;
SPACE:
	MVI	A,' '
	JMP	TYPE
;
;Print a dim '|'
;
ASTER:
	CALL	STNDOUT	;DIM
	MVI	A,'|'
	CALL	TYPE
	JMP	STNDEND	;BRIGHT
;
;Inline print routine
;  Print Chars ending in 0 pted to by Return Address; return to byte after
;
ILPRT:
	XTHL		;PT TO STRING
ILPLP:
	CALL	CTLCS	;ABORT?
	JZ	PRMPTR
	MOV	A,M	;GET CHAR
;	CPI	1	;PAUSE? -- ^A
;	JNZ	ILPOK
;	CALL	CONIN	;WAIT FOR ANY CHAR
;	CPI	3	;ABORT?
;	JZ	PRMPTR
;	JMP	ILPNX
;
;ILPOK:
	CPI	DIM	;GOTO DIM?
	JZ	ILPDIM
	CPI	BRIGHT	;GOTO BRIGHT?
	JZ	ILPBRI
	CALL	TYPE	;PRINT CHAR
	JMP	ILPNX
ILPDIM:
	CALL	STNDOUT	;ENTER STANDOUT MODE
	JMP	ILPNX
ILPBRI:
	CALL	STNDEND	;EXIT STANDOUT MODE
;
ILPNX:
	INX	H	;PT TO NEXT
	MOV	A,M	;GET IT
	ORA	A	;DONE?
	JNZ	ILPLP
	INX	H	;PT TO BYTE AFTER ENDING 0
	XTHL		;RESTORE HL AND RET ADR
	RET
;
;DISP calls DECIN, and validates a sector
;displacement, then converts it to an address
;
DISP:
	CALL	DECIN
DISP1:
	PUSH	PSW	;SAVE DELIMITER
	MOV	A,D
	ORA	A
	JNZ	BADISP
	MOV	A,E
	ORA	A
	JM	BADISP
	ADI	80H	;TO POINT TO BUFFER AT BASE+80H
	MOV	E,A
	MVI	D,BASE/256
	POP	PSW	;GET DELIM
	RET
;
BADISP:
	CALL	ILPRT
	DB	'++ Bad Displacement (Not 0-7FH) ++'
	DB	CR,LF,0
	JMP	PRMPTR
;
;Input Number from Command Line -- Assume it to be Hex
;  Number returned in DE
;
HEXIN:
	LXI	D,0	;INIT VALUE
	MOV	A,M
	CPI	'#'	;DECIMAL?
	JZ	HDIN	;MAKE DECIMAL
;
HINLP:
	MOV	A,M	;GET CHAR
	CALL	UPCASE	;CAPITALIZE
	CPI	CR	;EOL?
	RZ
	CPI	EOLCH	;EOL?
	RZ
	CPI	SEPCH
	RZ
	CPI	' '	;SPACE?
	RZ
	CPI	'-'	;'THRU'?
	RZ
	CPI	'>'
	RZ
	INX	H	;PT TO NEXT CHAR
	CPI	'0'	;RANGE?
	JC	WHAT
	CPI	'9'+1	;RANGE?
	JC	HINNUM
	CPI	'A'	;RANGE?
	JC	WHAT
	CPI	'F'+1	;RANGE?
	JNC	WHAT
	SUI	7	;ADJUST FROM A-F TO 10-15
;
HINNUM:
	SUI	'0'	;CONVERT FROM ASCII TO BINARY
	XCHG
	DAD	H	;MULT PREVIOUS VALUE BY 16
	DAD	H
	DAD	H
	DAD	H
	ADD	L	;ADD IN NEW DIGIT
	MOV	L,A
	XCHG
	JMP	HINLP
;
HDIN:
	INX	H	;SKIP '#'
;
;Input Number in Command Line as Decimal
;  Number is returned in DE
;
DECIN:
	LXI	D,0
	MOV	A,M	; GET 1ST CHAR
	CPI	'#'	; HEX?
	JNZ	DINLP
	INX	H	; PT TO DIGIT
	JMP	HINLP	; DO HEX PROCESSING
;
DINLP:
	MOV	A,M	;GET DIGIT
	CALL	UPCASE	;CAPITALIZE
	CPI	'0'	;RANGE?
	RC
	CPI	'9'+1	;RANGE?
	RNC
	SUI	'0'	;CONVERT TO BINARY
	INX	H	;PT TO NEXT
	PUSH	H
	MOV	H,D
	MOV	L,E
	DAD	H	;X2
	DAD	H	;X4
	DAD	D	;X5
	DAD	H	;X10
	ADD	L	;ADD IN DIGIT
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	XCHG		;RESULT IN DE
	POP	H
	JMP	DINLP
;
;Read in a console buffer
;
RDBUF:
	CALL	ILPRT	;PRINT PROMPT
	DB	CR,LF,'DU3  ',0
	LDA	DRIVE	;GET DRIVE NUMBER
	ADI	'A'	;CONVERT TO ASCII
	CALL	TYPE
	LDA	UNUM	;DISPLAY USER NUMBER
	MOV	L,A	;VALUE IN HL
	MVI	H,0
	CALL	DEC	;PRINT IN DECIMAL
	CALL	ILPRT	;PRINT PROMPT
	DB	'? ',0
;
;ENTRY POINT TO READ BUFFER WITHOUT PROMPT
;
RDBUF1:
	LHLD	INBUF	;USE CP/M READLN
	DCX	H
	DCX	H
	XCHG
	MVI	C,10
	PUSH	D
	CALL	BDOS
	POP	D
	INX	D	;PT TO CHAR COUNT
	LDAX	D	;GET CHAR COUNT
	MOV	B,A	;CHAR COUNT IN B
	INX	D	;PT TO INPUT LINE
	XCHG		;... IN HL
	ADD	L	;ADD CHAR COUNT TO HL
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	MVI	A,CR	;STORE ENDING CR
	MOV	M,A	;SET CR
	CALL	TYPE	;ECHO IT
	MVI	A,LF	;ECHO..
	CALL	TYPE	;..LF
	LHLD	INBUF	;SET PTR TO FIRST CHAR IN LINE
	RET
;
;Set paging flag for page routine
;
PAGSET:
	LDA	PAGSIZ	;GET SIZE OF PAGE
	STA	PAGFLG	;SET FLAG
	RET
;
;Page output
;
PAGER:
	LDA	PAGFLG	;GET FLAG
	CPI	2	;2 LINES LEFT?
	JZ	WAIT	;SAME AS USER DELAY
	DCR	A	;COUNT DOWN
	STA	PAGFLG
	JMP	CRLF
;
;Delay Routine
;
SWAIT:
	CALL	AT
	DB	23,5	;POSITION CURSOR
	JMP	WAIT0
WAIT:
	CALL	CRLF	;NEW LINE
WAIT0:
	PUSH	H
	CALL	ILPRT
	DB	DIM,'Type Any Character to Continue or ^C to Abort - ',BRIGHT,0
	POP	H
	CALL	CONIN	;GET RESPONSE
	CPI	'C'-40H	;^C?
	JZ	WAIT1
	CALL	CRLF	;NEW LINE
	CALL	PAGSET	;RESET PAGE COUNT
	RET
WAIT1:
	LDA	IHFLG	;INITIAL HELP?
	ORA	A	;0=NO
	JZ	PRMPTR	;ABORT TO COMMAND PROMPT
	JMP	EXIT1	;ABORT TO CP/M
;
;CRLF Routine
;
CRLF:
	MVI	A,CR
	CALL	TYPE
	MVI	A,LF
	JMP	TYPE
;
;Convert to Upper Case
;
UPCASE:
	ANI	7FH	;MASK OUT MSB
	CPI	60H	;LESS THAN SMALL A?
	RC		;RETURN IF SO
	ANI	5FH	;MAKE UPPER CASE
	RET
;
;CON: Status Routine
;
CONST:
	PUSH	B
	PUSH	D
	PUSH	H
VCONST:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	POP	D
	POP	B
	RET
;
;CON: Input Routine
;
CONIN:
	PUSH	B
	PUSH	D
	PUSH	H
VCONIN:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	POP	D
	POP	B
	RET
;
;Console out with TAB expansion
;  Char in A
;
TYPE:
	PUSH	B	;SAVE REGS
	PUSH	D
	PUSH	H
	MOV	C,A	;FOR OUTPUT ROUTINE
	CPI	TAB
	JNZ	TYPE2
;Tabulate
TYPTAB:
	MVI	A,' '	;PRINT SPACE
	CALL	TYPE
	LDA	TABCOL	;GET COL COUNT
	ANI	7	;DONE?
	JNZ	TYPTAB
	JMP	TYPRET
;
;Filter out control characters to
;prevent garbage during view of file
;
TYPE2:
	CPI	' '
	JNC	TYPEQ
	CPI	CR
	JZ	TYPEQ
	CPI	LF
	JNZ	TYPNCR
;
TYPEQ:
;
;CON: Output Routine
;
VCONOT:	CALL	$-$	;ADDR FILLED IN BY 'INIT'
;
;Update column used in tab expansion
;
	MOV	A,C	;GET CHAR
	CPI	CR
	JNZ	TYPNCR
	MVI	A,0	;RESET TAB COLUMN IF <CR>
	STA	TABCOL
	JMP	TYPLST
;
TYPNCR:
	CPI	' '	;CTL CHAR?
	JC	TYPLST	;..NO CHANGE IN COL
	LDA	TABCOL	;INCR TAB COUNT
	INR	A
	STA	TABCOL
;
TYPLST:
	LDA	PFLAG	;CHECK FOR PRINTER OUTPUT
	ANI	1
	CNZ	LIST	;FROM C REG
;
TYPRET:
	POP	H	;RESTORE REGS
	POP	D
	POP	B
	RET
;
;LST: Output Routine
;  Char in C
;
LIST:
	PUSH	B	;SAVED REGS
	PUSH	D
	PUSH	H
VLIST:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	POP	D
	POP	B
	RET
;
;Home Disk Routine
;
HOME:
	PUSH	H
VHOME:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	RET
;
;Set track # in DE
;
SETTRK:
	PUSH	H
	LHLD	MAXTRK	;CHECK FOR WITHIN BOUNDS
	CALL	SUBDE	;IF TRACK # IN DE > MAX, THEN ERROR
	POP	H
	JC	OUTLIM
	XCHG		;RESET CURRENT TRACK
	SHLD	CURTRK
	XCHG
	MOV	B,D	;BC=TRACK NUMBER
	MOV	C,E
	PUSH	H
;
VSETRK:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H
	RET
;
;Set Sector Number in DE
;
SETSEC:
	PUSH	H
	PUSH	D
	LHLD	SYSTRK	;GET NUMBER OF SYSTEM TRACKS
	XCHG
	SHLD	CURSEC	;SET CURRENT SECTOR
	LHLD	CURTRK	;GET CURRENT TRACK
	CALL	SUBDE	;SEE IF WE ARE IN THE SYSTEM TRACKS
	POP	B	;BC=SECTOR NUMBER
	MOV	H,B	;HL=SECTOR NUMBER
	MOV	L,C
	JNC	NOTSYS	;IF NO CARRY FOR SUBDE, WE ARE NOT IN SYSTEM TRACKS
	LDA	FIRST0	;SEE IF FIRST SEC 0
	ORA	A
	JNZ	GSTSEC	;NO, JUMP AWAY
	DCX	H	;YES, SO DECREMENT
	JMP	GSTSEC	;REQUESTED, THEN GO
;
;Not in System Tracks, so Skew Factor is effective
;
NOTSYS:
	LHLD	SECTBL	;GET PTR TO SECTOR TABLE
	XCHG		;... IN DE
	DCX	B	;DECREMENT SECTOR NUMBER BY 1
;
VSCTRN:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	LDA	SPT+1	;IF SPT<256 (HI-ORD = 0)
	ORA	A	; THEN FORCE 8-BIT TRANSLATION
	JNZ	GSTSEC	; ELSE KEEP ALL 16 BITS
	MOV	H,A
GSTSEC:
	SHLD	PHYSEC
	MOV	B,H
	MOV	C,L
;
VSTSEC:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	POP	H	;RESTORE PTR TO NEXT CHAR
	RET
;
;Out of Disk Track Limit
;
OUTLIM:
	CALL	ILPRT
	DB	'++ Not Within Tracks 0-',0
	PUSH	H
	LHLD	MAXTRK	;PRINT MAX TRACK NUMBER
	CALL	DEC
	POP	H
	CALL	ILPRT
	DB	' ++',CR,LF,0
	CALL	NORITE	;NOT POSITIONED
	JMP	PRMPTR
;
;Set DMA Address
;
SETDMA:
	JMP	$-$	;ADDR FILLED IN BY 'INIT'
;
;Read Next Block into DMA Address
;
READ:
	MVI	A,1	;SET FLAG
	STA	WRFLG
	PUSH	H	;SAVE PTR TO NEXT CHAR
;
VREAD:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	ORA	A	;ERROR?
	JZ	READOK
	CALL	ILPRT
	DB	'++ READ Failed, Sector may be Invalid ++'
	DB	CR,LF,0
;
READOK:
	POP	H	;GET PTR TO NEXT CHAR
	RET
;
;Write Block in DMA Address to Disk
;
WRITE:
	LDA	WRFLG	;READ ALREADY PERFORMED?
	ORA	A	;ERROR IF NOT
	JNZ	PWRITE
;
BADW:
	CALL	ILPRT
	DB	'++ Cannot Write Unless Read Issued ++'
	DB	CR,LF,0
	JMP	EXPL
;
;Do Write
;
PWRITE:
	PUSH	H	;SAVE PTR TO NEXT CHAR
	MVI	C,1	;FORCE WRITE TYPE 1 IN CASE 2.x DEBLOCK USED
;
VWRITE:
	CALL	$-$	;ADDR FILLED IN BY 'INIT'
	ORA	A	;ERROR?
	JZ	WRITOK
	CALL	ILPRT
	DB	'++ WRITE Failed ++',CR,LF,0
;
WRITOK:
	POP	H
	RET
;
;Help; HELP is entry point for HELP (?) command, HELP1 is entry point for
; Initial Help Command, and IHELP is entry point for HELP (/) from command
; line
;
IHELP:
	call	cls
	cz	crlf
	call	helpban
	CALL	ILPRT
	DB	DIM,'Introductory HELP on DU3 (Disk Utility)',BRIGHT,CR,LF
	DB	'  The DU3 program is designed to provide the user with'
	DB	CR,LF
	DB	'the ability to manipulate information on the disk as easily'
	DB	CR,LF
	DB	'as the DDT and SID utilities allow the user to manipulate'
	DB	CR,LF
	DB	'information in memory.',CR,LF
	DB	'  The following is a summary of the commands available to'
	DB	CR,LF
	DB	'the DU3 user.  This same list is invoked internally by the'
	DB	CR,LF
	DB	'? Command of DU3.  For additional information on disk'
	DB	CR,LF
	DB	'structures and how to use DU3 in general, refer to the'
	DB	CR,LF
	DB	'file DU3.HLP.',CR,LF,0
	MVI	A,0FFH	;A=0FFH
	STA	IHFLG	;SET INITIAL HELP
	CALL	SWAIT
	JMP	HELP1	;PROCESS NORMALLY
HELP:
	XRA	A	;A=0
	STA	IHFLG	;SET NO INITIAL HELP
HELP1:
	call	cls
	cz	crlf
	call	helpban	;print help banner
	CALL	ILPRT
	DB	'Operands in brackets [...] are optional'
	DB	CR,LF,CR,LF
	DB	'	@	',DIM,'Repeat Previous Non-@ Command Line'
	DB	BRIGHT,CR,LF
	DB	'	+[nn]	',DIM,'Step In [nn (decimal)] Sectors'
	DB	BRIGHT,CR,LF
	DB	'	-[nn]	',DIM,'Step Out [nn (decimal)] Sectors'
	DB	BRIGHT,CR,LF
	DB	DIM
	DB	'	 Note:  + or - need not be followed by a "," to '
	DB	'delimit commands.'
	DB	BRIGHT
	DB	CR,LF
	DB	'	#	',DIM,'Print Disk Parameters for Current Drive'
	DB	BRIGHT
	DB	CR,LF
	DB	'	=xxx	',DIM
	DB	'Search for ASCII xxx from Current Sector'
	DB	BRIGHT
	DB	CR,LF
	DB	DIM
	DB	'	 Note:  upper/lower case matters.  Use <xx> for hex:'
	DB	BRIGHT
	DB	CR,LF
	DB	DIM
	DB	'	  To find "IN 0" use: =<db><0>     or'
	DB	BRIGHT
	DB	CR,LF
	DB	DIM
	DB	'	  "(tab)H,0(CR)(LF)" use: =<9>H,0<D><A>'
	DB	BRIGHT
	DB	CR,LF
	DB	'	*[nn]	',DIM,'Repeat [nn (decimal) times]'
	DB	BRIGHT,CR,LF
	DB	'	!	',DIM,'Pause for User',BRIGHT,CR,LF
	DB	'	:ntext	',DIM,'Define ''text'' to be Macro n'
	DB	BRIGHT,CR,LF
	DB	'	n	',DIM,'Perform Macro n, 0<=n<=9',BRIGHT,CR,LF
	DB	'	:Pn	',DIM,'Print Macro n, 0<=n<=9',BRIGHT
	DB	CR,LF
	DB	'	:Px	',DIM
	DB	'Print All Macros if x=A or Print Prev Line if x=@'
	DB	BRIGHT
	DB	0
	CALL	SWAIT
	call	cls
	cz	crlf
	call	helpban
	CALL	ILPRT
	DB	'	A[ff,tt]	',DIM,'ASCII Dump',BRIGHT
	DB	CR,LF
	DB	'	C		',DIM,'Change:',BRIGHT
	DB	CR,LF
	DB	'		 	 CHaddr byte byte... (hex)'
	DB	CR,LF
	DB	'		 	 ',DIM,'or',BRIGHT
	DB	' CAaddr data...  (Ascii)'
	DB	CR,LF
	DB	DIM
	DB	'		 		<xx> Allowed for imbedded hex.'
	DB	BRIGHT
	DB	CR,LF
	DB	'		 	 ',DIM,'or',BRIGHT
	DB	' CHfrom-thru byte  e.g. ch0-7f e5'
	DB	CR,LF
	DB	'		 	 ',DIM,'or',BRIGHT,' CAfrom-thru byte'
	DB	CR,LF
	DB	'	D[ff,tt]	',DIM,'Dump (Hex and ASCII)',BRIGHT
	DB	CR,LF
	DB	'	E               ',DIM,'DU3 Editor',BRIGHT,CR,LF
	DB	'	                ',DIM,' Note: Rest of Command Line '
	DB	'is Flushed',BRIGHT,CR,LF
	DB	'	Ffn.ft		',DIM,'Find File',BRIGHT
	DB	CR,LF
	DB	'	Gnn		',DIM,'CP/M Allocation Group nn (hex)'
	DB	BRIGHT,CR,LF
	DB	'	H[ff,tt]	',DIM,'Hex Dump',BRIGHT
	DB	CR,LF
	DB	'	L[d]		',DIM,'Log in Current Drive or Drive d'
	DB	BRIGHT,CR,LF
	DB	'	M[nn]		',DIM,'Map [from group nn (hex)]'
	DB	BRIGHT
	DB	0
	CALL	SWAIT
	call	cls
	cz	crlf
	call	helpban
	CALL	ILPRT
	DB	'	N	',DIM,'Load New Disk',BRIGHT,CR,LF
	DB	'	P	',DIM,'Toggle Printer Switch',BRIGHT,CR,LF
	DB	'	Q	',DIM,'Queue Status;',BRIGHT,CR,LF
	DB	'	QZ	',DIM,'Zero (Empty) Queue',BRIGHT,CR,LF
	DB	'	QSfn.ft ',DIM,'Save Queue as a File on Disk',BRIGHT
	DB	CR,LF
	DB	'	<	',DIM,'Save Current Block into Temp',BRIGHT
	DB	CR,LF
	DB	'	>	',DIM,'Restore Temp Block',BRIGHT,CR,LF
	DB	'	<B	',DIM,'Save Block into Queue',BRIGHT,CR,LF
	DB	'	>B	',DIM,'Restore Queue Block',BRIGHT,CR,LF
	DB	'	<G[n]	',DIM,'Save Group into Queue',BRIGHT,CR,LF
	DB	'	>G[n]	',DIM,'Restore Queue Group',BRIGHT,CR,LF
	DB	'	Snn	',DIM,'Sector nn (decimal)',BRIGHT,CR,LF
	DB	'	Tnn	',DIM,'Track nn (decimal)',BRIGHT,CR,LF
	DB	'	Unn	',DIM,'Set User nn (decimal) for Find command'
	DB	BRIGHT,CR,LF
	DB	'	V[nn]	',DIM,'View [nn (decimal)] ASCII Blocks',BRIGHT
	DB	CR,LF
	DB	'	R	',DIM,'Read Current Block',BRIGHT,CR,LF
	DB	'	W	',DIM,'Write Current Block',BRIGHT,CR,LF
	DB	'	X	',DIM,'Exit Program',BRIGHT,CR,LF
	DB	'	Z[nn]	',DIM,'Sleep [nn (decimal) seconds]',BRIGHT
	DB	0
	CALL	SWAIT
	call	cls
	cz	crlf
	call	helpban
	CALL	ILPRT
	DB	DIM,'Command Line is of the form:  ',BRIGHT,'DU3 du?',CR,LF
	DB	'	',DIM,'"d" is Logged-In Disk, "u" is Current User'
	DB	BRIGHT,CR,LF
	DB	CR,LF
	DB	'Ctrl-C	',DIM,'Cancel a function',BRIGHT,CR,LF
	DB	'Ctrl-S	',DIM,'Suspend output',BRIGHT,CR,LF
	DB	'","	',DIM,'Separate commands',BRIGHT,CR,LF
	DB	CR,LF
	DB	DIM,'Examples:',BRIGHT,' g0 ',DIM,'and',BRIGHT
	DB	' +,d,z2,*'
	DB	CR,LF
	DB	DIM,'Causes:',BRIGHT,CR,LF
	DB	'	1. Position to group 0',CR,LF
	DB	'	2. Loops on step in, dump, sleep 2 sec',CR,LF
	DB	'		until control-c is typed',CR,LF,CR,LF
	DB	'"nn" usage varies with command as follows:',CR,LF
	DB	'	+, -, *, T, S, U, V, Z	',DIM,'nn in Decimal'
	DB	BRIGHT,CR,LF
	DB	'				',DIM,'(use #nn for Hex)'
	DB	BRIGHT,CR,LF
	DB	'	G, M			',DIM,'nn in Hexadecimal'
	DB	BRIGHT,CR,LF
	DB	'				',DIM,'(use #nn for Decimal)'
	DB	BRIGHT,CR,LF
	DB	CR,LF
	DB	'"ff" and "tt" are in Hexadecimal (use #ff or #tt for Decimal)'
	DB	0
	CALL	SWAIT
	LDA	IHFLG	;INITIAL HELP?
	ORA	A	;0=NO
	JNZ	EXIT1	;RETURN TO CP/M IF SO
	JMP	PRMPTR	;NEW LINE INPUT IF NOT
helpban:
	call	ilprt
	DB	'      ',DIM,'-- DU3 Command Summary --',BRIGHT
	DB	CR,LF,CR,LF,0
	ret
;
;COMMAND:  X
;Exit to CP/M
;
EXIT:
	call	dinit	;deinit terminal
	JMP	BASE	;WARM BOOT
;
;Quick Exit to CP/M
;
EXIT1:
	LHLD	DUTSTK	;GET CP/M STACK PTR
	SPHL		;SET SP
	RET

;
;********************************
;*				*
;*    Utility Subroutines	*
;*				*
;********************************
;
GRPCMP:
	MOV	A,C
	INR	D
	DCR	D
	JZ	CMP8
	CMP	M
	INX	H
	RNZ
	MOV	A,B
;
CMP8:
	CMP	M
	RET
;
;2's complement HL ==> HL
;
NEG:
	MOV	A,L
	CMA
	MOV	L,A
	MOV	A,H
	CMA
	MOV	H,A
	INX	H
	RET
;
;HL/2 ==> HL
;
ROTRHL:
	ORA	A
	MOV	A,H
	RAR
	MOV	H,A
	MOV	A,L
	RAR
	MOV	L,A
	RET
;
;Collect the number of '1' bits
;in A as a count in C
;
COLECT:
	MVI	B,8	;NUMBER OF BITS
;
COLOP:
	RAL
	JNC	COSKIP
	INR	C
;
COSKIP:
	DCR	B
	JNZ	COLOP
	RET
;
;HL-DE ==> HL
;  Carry Flag is Significant
;
SUBDE:
	MOV	A,L
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	RET
;
;Quick Kludge multiply
;HL*DE ==> HL
;
MULT:
	PUSH	B
	PUSH	D
	XCHG
	MOV	B,D
	MOV	C,E
	MOV	A,B
	ORA	C
	JNZ	MULCON
	LXI	H,0	;FILTER SPECIAL CASE
	JMP	MLDONE	;  OF MULTIPLY BY 0
;
MULCON:
	DCX	B
	MOV	D,H
	MOV	E,L
;
MULTLP:
	MOV	A,B
	ORA	C
	JZ	MLDONE
	DAD	D
	DCX	B
	JMP	MULTLP
;
MLDONE:
	POP	D
	POP	B
	RET
;
;Routine to fill in disk params
;with every drive change
;
LOGIT:
	LXI	D,DPB	;   THEN MOVE TO LOCAL
	MVI	B,DPBLEN ;  WORKSPACE
	CALL	MOVE
	LXI	H,GRPDIS
	MOV	A,M
	PUSH	PSW
	LDA	BLM
	MOV	M,A
	PUSH	H
	LHLD	DSM
	XCHG
	CALL	GTKSEC
	SHLD	MAXSEC
	XCHG
	SHLD	MAXTRK
	POP	H
	POP	PSW
	MOV	M,A
	RET

;***********************************
;
;  DU3 Command Table
;
;***********************************
CMDTBL:
	DB	' '	;null command
	DW	PROMPT
;
	DB	':'
	DW	MAC
;
	DB	'@'
	DW	PCMD
;
	DB	'+'	
	DW	 PLUS
;
	DB	'-'
	DW	MINUS
;
	DB	'='
	DW	SEARCH
;
	DB	'<'
	DW	SAVE
;
	DB	'>'
	DW	RESTOR
;
	DB	'#'
	DW	STATS
;
	DB	'?'
	DW	HELP
;
	DB	MULCH
	DW	REPEAT
;
	DB	'!'
	DW	UWAIT
;
	DB	'A'
	DW	DUMP
;
	DB	'C'
	DW	CHG
;
	DB	'D'
	DW	DUMP
;
	DB	'E'
	DW	EDIT
;
	DB	'F'
	DW	POSFIL
;
	DB	'G'
	DW	POS
;
	DB	'H'
	DW	DUMP
;
	DB	'L'
	DW	LOGIN
;
	DB	'M'
	DW	MAP
;
	DB	'N'
	DW	NEWDSK
;
	DB	'P'
	DW	PRNTFF
;
	DB	'Q'
	DW	QUEUER
;
	DB	'R'
	DW	DOREAD
;
	DB	'S'
	DW	POS
;
	DB	'T'
	DW	POS
;
	DB	'U'
	DW	USER
;
	DB	'V'
	DW	VIEW
;
	DB	'W'
	DW	DORITE
;
	DB	'X'
	DW	EXIT
;
	DB	'Z'
	DW	SLEEP
;
	DB	0	; End of Table
;*************************************

;
;Temporary storage area
;
clock:
	ds	1	;clock speed
pagsiz:
	ds	1	;page size
muser:
	ds	1	;max user
mdisk:
	ds	1	;max disk
STKSAV:
	DS	2	;SAVE HL VALUE
DUTSTK:
	DS	2	;OLD CP/M STACK POINTER; TOP OF DU3 STACK
BUFAD:
	DS	2	;FORCES INITIAL READ
QCNT:
	DS	2	;NUMBER OF SECTORS IN QUEUE
QNXT:
	DS	2	;PTR TO NEXT SECTOR IN QUEUE
QLST:
	DS	2	;PTR TO LAST SECTOR IN QUEUE
QPTR:
	DS	2	;G-P QUEUE PTR
HEXAD:
	DS	2	;TO RE-FETCH A VALUE
TOGO:
	DS	2	;REPEAT COUNT (FFFF=CONT)
TWOUP:
	DS	1
UNUM:
	DS	1	;NUMBER OF CURRENT USER
ONLY1:
	DS	1	;FLAG TO PRINT ONLY 1 MAP ENTRY (0=NO)
MFPTR:
	DS	2	;MULTI FILE PTR FOR GETGRP
PAGFLG:
	DS	1	;LINE COUNTER FOR PAGING
PFLAG:
	DS	1	;1=PRINT
GROUP:
	DS	2	;GROUP NUMBER
GRPDIS:
	DS	1	;DISPLACEMENT INTO GROUP
SAVEFL:
	DS	1	;SAVE FLAG
CURTRK:
	DS	2	;CURRENT TRACK NUMBER
CURSEC:
	DS	2	;CURRENT SECTOR NUMBER
PHYSEC:
	DS	2	;CURRENT PHYSICAL SECTOR NUMBER
TABCOL:
	DS	1	;TAB COLUMN
CPYFCT:
	DS	1	;GROUP COPY FUNCTION; 0=READ, 0FFH=WRITE
FILECT:
	DS	2	;FILE COUNT
DIRPOS:
	DS	1	;POSITION IN DIRECTORY
FINDFL:
	DS	1	;1=MUST POSITION AFTER FIND
FTSW:
	DS	1	;SEARCH W/O INCREMENT
NOTPOS:
	DS	1	;INITIALLY NOT POSITIONED
WRFLG:
	DS	1	;MAY NOT WRITE UNTIL '+', '-',
;			 OR 'G' COMMAND
TGRP:
	DS	2	;TEMPORARY GROUP FLAG
FIRST0:
	DS	1	;SETS TO 0 IF FIRST SEC # IS 0
DRIVE:
	DS	1	;DRIVE NUMBER
MAXTRK:
	DS	2	;MAX TRACK NUMBER
MAXSEC:
	DS	2	;MAX SECTOR NUMBER
SECTBL:
	DS	2	;POINTER TO SECTOR SKEW TABLE
;
IHFLG:
	DS	1	;0=NOT AT INITIAL HELP, 0FFH=AT INITIAL HELP
DUPFLG:
	DS	1	;SPACE OR STAR TO INDICATE MULTIPLE USERS
BACK:
	DS	2	;TO BACK UP IN "CA0-7F,X"
DUMTYP:
	DS	1
;
;The disk parameter block
;is moved here from CP/M
;
DPB	EQU	$	;DISK PARAMETER BLOCK (COPY)
SPT:
	DS	2
BSH:
	DS	1
BLM:
	DS	1
EXM:
	DS	1
DSM:
	DS	2
DRM:
	DS	2
AL0:
	DS	1
AL1:
	DS	1
CKS:
	DS	2
SYSTRK:
	DS	2
;
;End of disk parameter block
;
SAVBUF:
	DS	2
INBUF:
	DS	2	;INPUT LINE BUFFER
PINBUF:
	DS	2	;PREVIOUS CONTENTS OF INPUT BUFFER
CTEMP:
	DS	2	;BUILD NEW COMMAND LINE BUFFER
CTEMPX:
	DS	2	;END OF CTEMP
MTABL:
	DS	2	;10 PAGES FOR 10 MACROS
GBUFF:
	DS	2
DIRECT:
	DS	2
;
	END
